/* CV interactivo isometrico — IvanLabs
   ===========================================================================
   Dos modos en la misma pagina: la oficina (canvas) y el CV en texto. El texto
   NUNCA se pinta sobre el escenario: va siempre sobre superficies propias
   (cabecera, ficha, panel de texto) con contraste medido CONTRA ESE FONDO, que
   es la regla dura del repo desde el 31-07-2026. */

:root {
    /* La pila de tipografia en un solo sitio: la usan esta hoja y el editor.
       Detras de Poppins van las del sistema, que es lo que se ve si la fuente
       no llega (y lo que evita que la pagina se quede muda). */
    --tipo: 'Poppins', system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

    /* --- Paleta IvanLabs -------------------------------------------------- */
    /* OJO: --primary NO vale como color de texto sobre fondos claros (2,8:1).
       Para texto va --primary-dark, que es el azul corregido del repo. */
    --primary: #569EF0;
    --primary-dark: #2769ab;      /* 5,68:1 sobre blanco · 5,10:1 sobre #eef3f9 */

    --tinta: #1c2733;             /* texto principal */
    --tinta-suave: #4a5866;       /* secundario; 5,6:1 sobre blanco */
    --papel: #ffffff;
    --papel-frio: #eef3f9;
    --fondo: #f4f6f8;
    --borde: #d3dde8;

    /* --- Escenario -------------------------------------------------------- */
    /* El pixel art tiene contornos oscuros y colores saturados: sobre un fondo
       oscuro se lee mucho mejor y no compite con el blanco de la interfaz. */
    --escena: #101b2e;
    --escena-borde: #24344f;
    --sobre-escena: #eaf2fd;      /* 14,2:1 sobre --escena */

    --foco: #ffb020;              /* anillo de foco: destaca sobre claro y oscuro */

    --radio: 10px;
    --sombra: 0 2px 10px rgba(16, 27, 46, .10);
}

* { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    height: 100%;
}

body {
    display: flex;
    flex-direction: column;
    background: var(--fondo);
    color: var(--tinta);
    font-family: var(--tipo);
    font-size: 16px;
    line-height: 1.6;
    /* El juego captura las flechas: si el navegador ademas hace scroll, el
       personaje se mueve y la pagina salta a la vez. */
    overscroll-behavior: none;
}

/* Foco visible SIEMPRE. No se elimina el outline en ningun sitio de esta hoja;
   se sustituye por uno propio mas grueso y con separacion. */
:focus-visible {
    outline: 3px solid var(--foco);
    outline-offset: 2px;
    border-radius: 4px;
}

/* --- Enlace de salto ------------------------------------------------------ */
/* Escondido hasta que recibe el foco: es la puerta de entrada al CV para quien
   navega con teclado. */
.salto {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 100;
    padding: .75rem 1.25rem;
    background: var(--papel);
    color: var(--primary-dark);
    font-weight: 600;
    border-radius: 0 0 var(--radio) 0;
}
.salto:focus {
    left: 0;
}

/* --- Cabecera ------------------------------------------------------------- */
.cabecera {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem 1.5rem;
    align-items: center;
    justify-content: space-between;
    padding: .75rem 1.25rem;
    background: var(--papel);
    border-bottom: 1px solid var(--borde);
}

.cabecera__marca {
    margin: 0;
    display: flex;
    flex-direction: column;
    line-height: 1.3;
}
.cabecera__marca strong { font-size: 1.05rem; }

/* El enlace a LinkedIn de la barra superior. Ocupa el sitio del antiguo
   subtitulo, pero a diferencia de aquel NO se esconde en movil: era texto
   decorativo y ahora es la via de contacto. El subrayado va como borde para
   que el icono no quede subrayado tambien. */
.cabecera__linkedin {
    display: inline-flex;
    align-items: center;
    align-self: flex-start;
    gap: .35rem;
    font-size: .85rem;
    font-weight: 600;
    color: var(--primary-dark);          /* 5,68:1 sobre el blanco de la barra */
    text-decoration: none;
    border-bottom: 1px solid rgba(39, 105, 171, .45);
}
.cabecera__linkedin:hover { border-bottom-color: currentColor; }
.cabecera__icono {
    width: 1.05em;
    height: 1.05em;
    flex: none;                          /* que el icono no se aplaste */
}

.cabecera__modos {
    display: flex;
    gap: .5rem;
}

.modo {
    padding: .5rem 1rem;
    font: inherit;
    font-size: .9rem;
    font-weight: 600;
    color: var(--primary-dark);
    background: var(--papel-frio);
    border: 1px solid var(--borde);
    border-radius: var(--radio);
    cursor: pointer;
}
.modo:hover { background: #e2ebf6; }

/* El modo activo no se distingue SOLO por color: lleva ademas aria-pressed y un
   subrayado grueso, para que se note tambien en escala de grises. */
.modo--activo {
    color: var(--papel);
    background: var(--primary-dark);   /* blanco sobre #2769ab = 5,68:1 */
    border-color: var(--primary-dark);
    box-shadow: inset 0 -3px 0 rgba(0, 0, 0, .25);
}
.modo--activo:hover { background: #205a91; }

/* El boton "Pantalla completa" del modo embebido es un <a>, no un <button>:
   hay que devolverle lo que el navegador le quita a un enlace con .modo. */
a.modo {
    display: inline-flex;
    align-items: center;
    text-decoration: none;
}

/* --- Modo embebido (?embed=1) ---------------------------------------------
   Lo enciende src/main.js cuando el CV se abre dentro de un <iframe> (hoy, la
   pagina "Sobre mi y CV" de ivanlabs.es). Solo esconde lo que la pagina de
   fuera ya ofrece mejor: su enlace a LinkedIn es mas grande que este, y el
   "Editar" del pie es la puerta del editor de Ivan, que dentro del campus no
   pinta nada.

   Lo que NO se esconde nunca son los creditos del arte: la licencia del
   paquete pide el nombre del autor y su enlace, y eso no depende de en que
   marco se vea la pagina. */
body.embebido .cabecera__linkedin,
body.embebido .pie__editar { display: none; }

/* --- Zona principal ------------------------------------------------------- */
.contenido {
    flex: 1 1 auto;
    min-height: 0;               /* sin esto el canvas desborda en flexbox */
    display: flex;
    flex-direction: column;
}

/* --- Modo oficina --------------------------------------------------------- */
.oficina {
    position: relative;
    flex: 1 1 auto;
    min-height: 0;
    background: var(--escena);
    overflow: hidden;
}

#lienzo {
    display: block;
    width: 100%;
    height: 100%;
    /* Pixel art: sin esto el navegador suaviza los sprites al escalarlos y el
       dibujo queda borroso y sucio. */
    image-rendering: pixelated;
    touch-action: none;          /* el mando tactil gestiona los dedos */
}

/* Pantalla de carga */
.cargando {
    position: absolute;
    inset: 0;
    display: grid;
    place-content: center;
    gap: 1rem;
    background: var(--escena);
    color: var(--sobre-escena);
    transition: opacity .3s ease;
}
.cargando[hidden] { display: none; }
.cargando__texto { margin: 0; font-weight: 600; }
.cargando__barra {
    width: min(260px, 60vw);
    height: 8px;
    background: var(--escena-borde);
    border-radius: 99px;
    overflow: hidden;
}
.cargando__barra span {
    display: block;
    height: 100%;
    width: 0;
    background: var(--primary);
    transition: width .2s ease;
}

/* Texto solo para lectores de pantalla. Se recorta en vez de ocultarse con
   display:none, que lo sacaria tambien del arbol de accesibilidad. */
.visualmente-oculto {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* --- HUD: cuanto llevas visto ---------------------------------------------
   Solo el progreso. El rotulo de la zona que pisabas se quito el 12-08-2026:
   ver el comentario del index.html. */
.hud {
    position: absolute;
    top: 1rem;
    left: 1.25rem;
    pointer-events: none;
}
.hud__progreso {
    margin: 0;
    padding: .3rem .8rem;
    border-radius: var(--radio);
    background: rgba(9, 16, 28, .92);
    width: max-content;
    font-size: .78rem;
    color: #b9cbe4;              /* 8,1:1 sobre el fondo del HUD */
}

/* Pista de interaccion */
.pista {
    position: absolute;
    left: 50%;
    bottom: 5.5rem;
    transform: translateX(-50%);
    margin: 0;
    max-width: min(90%, 34rem);
    padding: .5rem 1rem;
    text-align: center;
    color: var(--sobre-escena);
    background: rgba(9, 16, 28, .92);   /* opaco de verdad: el escenario no debe
                                           colarse por detras del texto */
    border: 1px solid var(--escena-borde);
    border-radius: var(--radio);
    opacity: 0;
    transition: opacity .15s ease;
    pointer-events: none;
}
.pista--visible { opacity: 1; }

/* Ayuda de teclado */
.ayuda {
    position: absolute;
    left: 50%;
    bottom: 1rem;
    transform: translateX(-50%);
    margin: 0;
    padding: .4rem .9rem;
    font-size: .8rem;
    color: var(--sobre-escena);
    background: rgba(9, 16, 28, .92);
    border-radius: var(--radio);
    white-space: nowrap;
}
.ayuda kbd,
.pista kbd {
    display: inline-block;
    padding: .05rem .35rem;
    margin: 0 .1rem;
    font: inherit;
    font-size: .78rem;
    background: #2b3d5c;
    border: 1px solid #48597a;
    border-bottom-width: 2px;
    border-radius: 4px;
}

/* --- Puntos de interes ----------------------------------------------------
   Botones de verdad colocados encima de su mueble. Invisibles mientras no se
   usen, para no ensuciar el escenario, pero SIEMPRE en el arbol de foco y en el
   de accesibilidad: son la via por la que se recorre el CV con teclado.
   Nada de display:none ni de visibility:hidden — eso los sacaria de los dos. */
.puntos {
    position: absolute;
    inset: 0;
    margin: 0;
    padding: 0;
    list-style: none;
    pointer-events: none;      /* solo los botones reciben el raton */
}
.puntos li { position: absolute; }

.punto {
    position: absolute;
    left: 0;
    top: 0;
    /* El transform lo pone el JS con la posicion del mueble; este margen negativo
       lo centra sobre el. */
    margin: -26px 0 0 -26px;
    width: 52px;
    height: 52px;
    padding: 0;
    /* El texto se oculta con color transparente y no con text-indent ni con
       font-size 0: asi sigue siendo el nombre accesible del boton. */
    color: transparent;
    font-size: 0;
    background: transparent;
    border: 2px solid transparent;
    border-radius: 50%;
    cursor: pointer;
    pointer-events: auto;
    transition: background-color .12s ease, border-color .12s ease;
}
.punto:hover {
    background: rgba(255, 176, 32, .18);
    border-color: rgba(255, 176, 32, .8);
}
/* El circulo de foco del rombo: SOLO mientras se recorre la pagina con Tab.
   ---------------------------------------------------------------------------
   Quien tabula lo necesita — es su unica pista de donde esta. Quien anda con
   WASD, no: ya tiene un muñeco en pantalla diciendoselo, y ahi el circulo se
   quedaba encendido sobre cada mueble visitado (`<dialog>` devuelve el foco al
   boton que lo abrio, y el navegador lo cuenta como foco de teclado).

   La regla NO es "quitar el foco visible", que seria romper la accesibilidad:
   es enseñarlo cuando la persona esta navegando con el foco. Lo dice la clase
   `tabulando` del <body>, que main.js enciende con Tab y apaga en cuanto hay un
   clic o un paso. Contado a fondo alli.

   El `:focus-visible` general de arriba se anula aqui para los rombos y se
   repone abajo: sin esto, el anillo naranja de tres pixeles seguiria saliendo
   por su cuenta. */
.punto:focus-visible { outline: none; }

body.tabulando .punto:focus-visible {
    background: rgba(255, 176, 32, .28);
    border-color: var(--foco);
    outline: 3px solid var(--foco);
    outline-offset: 3px;
}
body.tabulando .punto--especial:focus-visible {
    background: rgba(63, 208, 216, .28);
    border-color: #3fd0d8;
}

/* Los CACHARROS de la habitacion (el interruptor de la musica) llevan el mismo
   boton, pero en el cian de su rombo: al pasar por encima se ve que ese mueble
   no cuenta curriculum, hace algo. */
.punto--especial:hover {
    background: rgba(63, 208, 216, .20);
    border-color: rgba(63, 208, 216, .85);
}

/* --- El cartel que señala el altavoz al empezar ----------------------------
   Cuelga del mismo <li> que el boton del cacharro, asi que se mueve con la
   camara sin codigo extra (recolocar() ya lo coloca en cada fotograma).

   Se va con el primer paso, igual que la invitacion grande: en cuanto la
   persona anda, el mensaje ha cumplido y solo tapa la habitacion. Se va con una
   transicion y no de golpe porque desaparecer de golpe se lee como un fallo. */
.punto__invitacion {
    position: absolute;
    left: 0;
    top: 0;
    /* Colocado por el JS (ver recolocar() en interaccion.js): va A UN LADO del
       mueble y hacia fuera de la habitacion, a la altura de su rombo. */
    padding: 0;
    white-space: nowrap;
    font-size: .82rem;
    font-weight: 600;
    /* Letra blanca sobre el fondo del escenario, sin caja (Ivan, 12-08-2026):
       una etiqueta con relleno y borde competia con la habitacion justo cuando
       lo que se quiere es que mires el altavoz.
       La sombra NO es decorativa: el altavoz se puede mover a cualquier sitio
       desde /editor, y si acaba en mitad del cuarto este texto caeria sobre el
       suelo de madera. Es lo que lo mantiene legible en los dos fondos. */
    color: #ffffff;
    text-shadow: 0 1px 2px rgba(9, 16, 28, .95), 0 0 10px rgba(9, 16, 28, .85);
    background: none;
    border: none;
    pointer-events: none;              /* no se pulsa: se pulsa el mueble */
    opacity: 1;
    transition: opacity .45s ease;
}
/* La flecha: una linea fina horizontal que sale del texto hacia el mueble, con
   un chevron en la punta. Nada de triangulo relleno — el cartel ya no es una
   caja, y un triangulo suelto se lee como una mancha.

   Por defecto el cartel esta a la DERECHA del mueble, asi que la flecha sale
   por su izquierda y apunta a la izquierda. */
.punto__invitacion::after {
    content: "";
    position: absolute;
    top: 50%;
    right: 100%;
    width: 20px;
    height: 1px;
    margin-right: .4rem;
    background: rgba(255, 255, 255, .8);
}
.punto__invitacion::before {
    content: "";
    position: absolute;
    top: 50%;
    right: 100%;
    width: 6px;
    height: 6px;
    margin: -3px .4rem 0 0;
    border-left: 1px solid rgba(255, 255, 255, .8);
    border-bottom: 1px solid rgba(255, 255, 255, .8);
    transform: rotate(45deg);
    transform-origin: left bottom;
}

/* Y al reves cuando el cartel se va al otro lado: la flecha sale por su derecha
   y apunta a la derecha. Lo decide el JS con la posicion en pantalla. */
.punto__invitacion--izq::after {
    right: auto;
    left: 100%;
    margin: 0 0 0 .4rem;
}
.punto__invitacion--izq::before {
    right: auto;
    left: 100%;
    margin: -3px 0 0 .4rem;
    border-left: none;
    border-bottom: 1px solid rgba(255, 255, 255, .8);
    border-right: 1px solid rgba(255, 255, 255, .8);
    transform: rotate(-45deg);
    transform-origin: right bottom;
}
.punto__invitacion--fuera {
    opacity: 0;
}

/* --- Radio de la oficina ---------------------------------------------------
   Reproductor de la esquina: UNA barra horizontal con el nombre del tema y dos
   botones. El aire "pixel" no viene de una fuente nueva —eso serian mas bytes
   que servir— sino de tres cosas que ya estan en la pagina: esquinas a escuadra
   (nada de border-radius), un borde grueso con sombra dura de dos tonos (el
   chaflan de los botones de consola) y tipografia monoespaciada en versalitas.

   Antes esto era un panel plegable con lista de temas, barra de posicion y
   volumen (11-08-2026). Se simplifico el mismo dia: ver la cabecera de
   src/musica.js. El **volumen volvio** el 12-08-2026 —y solo el— porque desde
   que la musica arranca sola sin permiso, poder bajarla deja de ser un extra.
   La lista y la barra de posicion siguen fuera. */
.radio {
    position: absolute;
    right: 1.25rem;
    /* Justo ENCIMA de la linea de ayuda, no a su altura: esa linea no se parte
       (white-space: nowrap) y en cuanto la ventana baja de ~1100 px llega hasta
       la esquina, asi que compartir fila con ella era chocar seguro. */
    bottom: 3.6rem;
    z-index: 3;                          /* por encima del canvas y de la ayuda */
    display: flex;
    align-items: center;
    gap: .45rem;
    max-width: min(16rem, calc(100% - 2.5rem));
    padding: .35rem .45rem;
    font-family: ui-monospace, "Cascadia Mono", Consolas, "Courier New", monospace;
    font-size: .72rem;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--sobre-escena);          /* 13,9:1 sobre el fondo de la radio */
    background: #16233a;
    border: 2px solid #4a6fa5;
    /* Sombra dura, sin difuminar: es el borde biselado de los botones de las
       consolas viejas, y lo que separa la radio del escenario oscuro. */
    box-shadow: 0 3px 0 rgba(6, 11, 20, .85);
}
/* Y esta linea NO sobra, aunque lo parezca.
   `hidden` esconde las cosas con un `display: none` de la hoja del NAVEGADOR, y
   cualquier `display` que ponga la hoja de la pagina le gana — no por
   especificidad, sino porque los estilos de autor van por delante de los del
   navegador. Con el `display: flex` de arriba, poner `radio.hidden = true`
   desde el JS no escondia nada: la barra seguia en pantalla, apagada, como si
   el interruptor no funcionara. */
.radio[hidden] { display: none; }

/* El nombre del tema. Se lleva el hueco sobrante para que los botones queden
   siempre a la derecha, y recorta con puntos suspensivos si algun dia hay un
   titulo largo (min-width: 0, sin el cual el ellipsis no llega a actuar). */
.radio__titulo {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: .74rem;
}

/* El disco de vinilo. Decorativo (aria-hidden): quien no lo ve no se pierde
   nada, el estado tambien lo dicen el boton y el aviso en voz alta.

   Se dibuja a 18 px y no a 16 para que el rombo naranja del sello se vea; el
   SVG es vectorial, asi que escalarlo no lo emborrona. */
.radio__disco {
    flex: none;
    width: 18px;
    height: 18px;
    display: block;
    /* Apagado mientras no suena: sigue siendo un disco, pero se lee como parado
       (es el mismo recurso que usaba el ecualizador de la version anterior). */
    opacity: .55;
    transition: opacity .2s ease;
}
.radio--sonando .radio__disco {
    opacity: 1;
    /* steps(4): giros de 90 grados, nunca en continuo. El dibujo es simetrico a
       90 grados, asi que cada paso cae en las mismas casillas de pixel y el
       disco no se emborrona ni le baila el contorno; el brillo del borde es lo
       que hace ver el giro. Girarlo suave lo dejaria hecho un borron gris. */
    animation: radio-girar 1.6s steps(4, end) infinite;
}
@keyframes radio-girar {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}

/* --- Los dos botones --- */
.radio__boton {
    flex: none;
    min-width: 1.9rem;
    padding: .3rem .3rem;
    font: inherit;
    font-size: .7rem;
    color: var(--sobre-escena);
    background: #24365a;
    border: 2px solid #4a6fa5;
    cursor: pointer;
}
.radio__boton:hover { background: #30487a; }
.radio__boton:active { transform: translateY(1px); }
/* El de reproducir manda sobre el otro: es el que se busca con prisa. */
.radio__boton--play {
    min-width: 2.2rem;
    color: #14203a;
    background: var(--foco);             /* #14203a sobre #ffb020 = 9,6:1 */
    border-color: #ffd27a;
}
.radio__boton--play:hover { background: #ffc250; }

/* --- Volumen ---------------------------------------------------------------
   Un <input type="range"> repintado, no un control dibujado a mano: el teclado,
   el arrastre y el valor para el lector de pantalla ya vienen con el elemento.

   La parte llena de la barra se pinta con un degradado de dos paradas duras
   sobre la propia pista, cortado en `--relleno`, que lo escribe src/musica.js.
   No hay forma de conseguirlo solo con CSS, y `::-moz-range-progress` (que si
   existe) solo resuelve Firefox. Duras y no difuminadas: es el mismo criterio
   de esquinas a escuadra del resto de la radio. */
.radio__volumen {
    flex: none;
    width: 3.2rem;
    height: 1.1rem;
    margin: 0;
    padding: 0;
    appearance: none;
    -webkit-appearance: none;
    background: transparent;
    cursor: pointer;
}

/* El borde de la pista es MAS claro que el de los botones (#4a6fa5, 2,8:1 sobre
   el fondo de la radio). En un boton eso vale porque el relleno ya lo separa
   del fondo; aqui el borde ES el control, asi que sube a 5,0:1 para cumplir el
   3:1 de contraste de elementos no textuales. */
.radio__volumen::-webkit-slider-runnable-track {
    height: 8px;
    background: linear-gradient(to right,
        var(--foco) 0 var(--relleno, 35%), #2e4368 var(--relleno, 35%) 100%);
    border: 2px solid #6f93c8;
}
.radio__volumen::-moz-range-track {
    height: 8px;
    background: #2e4368;
    border: 2px solid #6f93c8;
}
.radio__volumen::-moz-range-progress {
    height: 8px;
    background: var(--foco);
    border: 2px solid #6f93c8;
    border-right: none;
}

/* Cuadrado, no redondo: aqui no hay una sola esquina redondeada.
   El `margin-top` es obligatorio en WebKit — el pulgar se alinea con el BORDE
   SUPERIOR de la pista, no con su centro, asi que hay que subirlo a mano
   (pista 12 px con bordes − pulgar 16 px) / 2 = −2 px. */
.radio__volumen::-webkit-slider-thumb {
    appearance: none;
    -webkit-appearance: none;
    width: 8px;
    height: 16px;
    margin-top: -2px;
    background: var(--sobre-escena);
    border: 2px solid #0b0f16;
}
.radio__volumen::-moz-range-thumb {
    width: 8px;
    height: 16px;
    border-radius: 0;
    background: var(--sobre-escena);
    border: 2px solid #0b0f16;
}

/* A cero el disco se apaga aunque la pista siga corriendo: sigue "sonando",
   pero no se oye, y un disco girando alegremente en silencio es una mentira. */
.radio--sonando.radio--muda .radio__disco { opacity: .5; }

/* En pantallas de dedo la esquina de abajo a la derecha ya es del boton
   "Mirar" del mando: la radio se sube arriba, debajo del HUD, donde no
   compite con nada. */
@media (pointer: coarse) {
    .radio {
        top: 1rem;
        right: 1rem;
        bottom: auto;
    }
    /* Y los botones crecen: 1,9rem es un objetivo comodo con raton, pero se
       queda corto para un dedo (las pautas hablan de ~44 px). */
    .radio__boton { min-width: 2.5rem; padding: .45rem .4rem; }
    /* El volumen tambien: con el dedo hay que poder recorrerlo, no acertarlo. */
    .radio__volumen { width: 4rem; height: 1.6rem; }
}

/* Mando tactil */
.mando {
    position: absolute;
    inset: auto 0 0 0;
    /* Por encima de la radio (z-index: 3). Es la unica forma de manejarse con el
       dedo: si algo se solapa con la palanca o con "Mirar", el que tiene que
       ceder es lo otro. */
    z-index: 4;
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    padding: 1rem 1.25rem calc(1rem + env(safe-area-inset-bottom));
    pointer-events: none;        /* solo los hijos reciben los dedos */
}
.mando[hidden] { display: none; }

.mando__base {
    position: relative;
    width: 120px;
    height: 120px;
    border-radius: 50%;
    background: rgba(9, 16, 28, .55);
    border: 2px solid var(--escena-borde);
    pointer-events: auto;
    touch-action: none;
}
.mando__palanca {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 52px;
    height: 52px;
    margin: -26px 0 0 -26px;
    border-radius: 50%;
    background: rgba(86, 158, 240, .85);
    border: 2px solid var(--sobre-escena);
}
.mando__accion {
    padding: 1rem 1.5rem;
    font: inherit;
    font-weight: 700;
    color: var(--papel);
    background: var(--primary-dark);
    border: 2px solid var(--sobre-escena);
    border-radius: 99px;
    pointer-events: auto;
    cursor: pointer;
}

/* --- Ficha (dialogo) ------------------------------------------------------ */
.ficha {
    width: min(38rem, calc(100vw - 2rem));
    max-height: min(80vh, 44rem);
    padding: 1.75rem;
    color: var(--tinta);
    background: var(--papel);
    border: none;
    border-radius: var(--radio);
    box-shadow: 0 12px 40px rgba(9, 16, 28, .35);
    overflow: auto;
}
.ficha::backdrop {
    background: rgba(9, 16, 28, .6);
}
/* Al quitar el aspa, dentro de la ficha ya no hay ningun control que tome el
   foco al abrirse (el boton de cerrar solo existe en tactil), asi que lo toma el
   propio <dialog> y se pintaba un anillo naranja alrededor de TODA la ficha.
   Ese foco es de programa: nadie llega ahi tabulando. Se quita solo en el
   contenedor —lo de dentro conserva el suyo— y no deja a nadie sin referencia,
   porque la ficha ya se recorta sola sobre el fondo oscurecido. */
.ficha:focus-visible { outline: none; }
/* El cierre de la ficha en pantallas tactiles: un boton con su nombre al pie,
   no un aspa en la esquina. Solo se ve donde hace falta (ver .solo-tactil). */
.ficha__cerrar {
    display: block;
    width: 100%;
    margin-top: 1.25rem;
    padding: .7rem;
    font: inherit;
    font-weight: 600;
    color: var(--tinta);
    background: var(--papel-frio);
    border: 1px solid var(--borde);
    border-radius: var(--radio);
    cursor: pointer;
}
.ficha__cerrar:hover { background: #e2ebf6; }

/* --- La ficha que lleva vídeo dentro (hoy, la de ivanlabs.es) --------------
   Un <iframe> de YouTube es de otro dominio: mientras tenga el foco se queda
   con el teclado y la E de cerrar no llega a la página. El foco al ABRIR ya lo
   arregla src/panel.js, pero en cuanto alguien le da al play el reproductor lo
   recupera —y así debe ser, sus atajos son suyos—, así que aquí la salida no
   puede depender solo de una tecla: el botón "Cerrar" (normalmente solo táctil)
   se enseña también con ratón, y la pista de abajo explica por qué. La clase la
   pone panel.js al abrir, y la quita al abrir cualquier otra ficha. */
.ficha--con-video .ficha__cerrar { display: block; }
.ficha__pista-video { display: none; }
/* En su propia linea, no pegado al "Pulsa E para cerrar" (peticion de Ivan,
   14-08-2026): son dos cosas distintas —el atajo normal y la excepcion del
   video— y en una sola linea se leian como una frase sola. */
.ficha--con-video .ficha__pista-video { display: block; margin-top: .4rem; }

.ficha__titulo {
    margin: 0 0 .25rem;
    font-size: 1.4rem;
    color: var(--primary-dark);
}

/* La seccion ("Experiencia profesional") va como antetitulo: en la oficina no
   hay ningun encabezado que la anuncie, asi que hay que reponer el contexto sin
   que parezca el primer parrafo de la ficha. */
.ficha__seccion {
    margin: 0 0 .75rem;
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--tinta-suave);
}

.ficha__cuerpo :last-child { margin-bottom: 0; }

/* El recordatorio de que E tambien cierra. Va al pie de la ficha, separado del
   contenido: es ayuda de la interfaz, no curriculum.

   A LA IZQUIERDA desde el 13-08-2026 (Ivan). Estaba alineado a la derecha, y en
   una ficha con acordeon quedaban las DOS ayudas de teclado —la del acordeon y
   esta— en esquinas opuestas, una debajo de la otra. Dos notas al pie que dicen
   lo mismo (que teclas pulsar) tienen que leerse como un bloque, no como dos
   cosas sueltas que no se saben mirar. */
.ficha__pista {
    margin: 1.25rem 0 0;
    padding-top: .75rem;
    font-size: .78rem;
    color: var(--tinta-suave);
    border-top: 1px solid var(--borde);
}
.ficha__pista kbd {
    display: inline-block;
    padding: .05rem .35rem;
    margin: 0 .1rem;
    font: inherit;
    font-size: .78rem;
    color: var(--tinta);
    background: var(--papel-frio);
    border: 1px solid var(--borde);
    border-bottom-width: 2px;
    border-radius: 4px;
}

/* --- Piezas del contenido del CV (compartidas por la ficha y el texto) ----- */
.subtitulo {
    margin: 0 0 .15rem;
    font-weight: 600;
    color: var(--tinta);
}
.titular {
    margin: 0 0 1rem;
    font-size: 1.1rem;
    color: var(--tinta-suave);
}
.fecha {
    margin-bottom: .75rem;
    font-size: .85rem;
    color: var(--tinta-suave);
}
/* --- Vídeo de una entrada -------------------------------------------------
   Lo pinta pintarVideo() (src/contenido.js) y sale en las dos caras del CV: la
   ficha de la oficina y el CV en texto.

   El hueco guarda la proporción con `aspect-ratio` y NO con un alto en píxeles.
   Dentro de la ficha el ancho cambia con la pantalla, así que un alto fijo deja
   franjas negras arriba y abajo en cuanto se estrecha. `width: 100%` + esta
   proporción y el iframe siempre encaja clavado. */
.video {
    margin: 1rem 0 0;
}
.video iframe {
    display: block;
    width: 100%;
    aspect-ratio: 16 / 9;
    border: 0;
    border-radius: var(--radio);
    background: #000;          /* mientras carga, negro y no blanco: es un vídeo */
}
.video__pie {
    margin: .4rem 0 0;
    font-size: .82rem;
    color: var(--tinta-suave);   /* 5,6:1 sobre el blanco de la ficha */
}
.video__pie a {
    color: var(--primary-dark);
    font-weight: 600;
}

.enlaces {
    margin: .75rem 0 0;
    padding: 0;
    list-style: none;
}
.enlaces li { margin-bottom: .25rem; }
.enlaces a {
    color: var(--primary-dark);
    font-weight: 600;
}

/* --- Grupos plegables (las entradas con `bloques`) ------------------------- */
/* Solo se ven asi en la FICHA de la oficina; en el CV en texto los mismos datos
   salen desplegados y con encabezados. El porque esta en contenido.js.

   Es <details>/<summary> nativo: el teclado, el estado para el lector de
   pantalla, el Ctrl+F dentro de lo plegado y el desplegado al imprimir vienen
   de serie. No hace falta el `aria-expanded` a mano que si necesitan los
   acordeones de los cursos, que son <div>. */
.bloques {
    margin-top: .9rem;
    border-top: 1px solid var(--borde);
}
.bloques .bloque { border-bottom: 1px solid var(--borde); }

.bloques summary {
    position: relative;
    padding: .55rem .2rem .55rem 1.5rem;
    cursor: pointer;
    /* Quita el triangulo del navegador (los dos, que cada motor usa el suyo):
       abajo se dibuja uno propio que se puede animar y colorear. */
    list-style: none;
}
.bloques summary::-webkit-details-marker { display: none; }

.bloques summary::before {
    content: '';
    position: absolute;
    left: .4rem;
    top: .95rem;
    width: .4rem;
    height: .4rem;
    border-right: 2px solid var(--primary-dark);
    border-bottom: 2px solid var(--primary-dark);
    transform: rotate(-45deg);
    transition: transform .15s ease;
}
.bloques .bloque[open] summary::before {
    top: .8rem;
    transform: rotate(45deg);
}

.bloques summary:hover { background: var(--papel-frio); }

.bloque__titulo {
    font-weight: 600;
    color: var(--tinta);
}
/* La fecha va en el resumen para poder situar cada pieza sin abrirla. En una
   pantalla ancha se va a la derecha; en uno estrecho cae debajo del titulo. */
.bloque__fecha {
    display: block;
    font-size: .82rem;
    color: var(--tinta-suave);   /* 5,6:1 sobre el blanco de la ficha */
}
@media (min-width: 30rem) {
    .bloques summary {
        display: flex;
        gap: .75rem;
        align-items: baseline;
        justify-content: space-between;
    }
    .bloque__fecha { flex: 0 0 auto; }
}

.bloque__cuerpo {
    padding: 0 .2rem .8rem 1.5rem;
}
.bloque__cuerpo > :first-child { margin-top: 0; }
.bloque__cuerpo :last-child { margin-bottom: 0; }

/* El atajo de teclado del acordeon. Discreto y al pie del grupo: es una ayuda,
   no un titular. Mismas teclas dibujadas que la linea de ayuda del escenario,
   para que se lean como lo mismo.

   TRES ARREGLOS DEL 13-08-2026 (Ivan: "le daria mas aire y mejor alineado"):

   1) AIRE. Nacia pegada al ultimo cajon del acordeon —.5rem— y parecia una
      fila mas de la lista en vez de una nota al pie. Ahora respira arriba y
      queda separada de lo que explica.
   2) ALINEACION VERTICAL. Las teclas eran `inline-block` sobre la linea base
      del texto y, con su relleno y su borde inferior de 2 px, colgaban por
      debajo: la frase se leia torcida. Con flex y `align-items: center` cada
      tecla se centra con la palabra que va al lado.
   3) A PLOMO CON LAS REGLAS DEL ACORDEON, no con el titulo de cada cajon. Se
      probaron las dos: sangrada 1.5rem se alinea con los titulos, pero entonces
      queda 27 px desplazada respecto al "Pulsa E para cerrar" que va justo
      debajo, y dos notas al pie casi iguales con distinta sangria se leen como
      un descuadre. Alineadas las dos al borde de la ficha —que es donde empiezan
      y acaban las lineas del acordeon— todo el pie cae en la misma vertical.

   Ojo con el `gap`: flex se COME los espacios sueltos entre los trozos de
   texto, asi que la separacion tiene que venir de aqui. Los espacios siguen
   escritos en el JS a proposito, para que si esta hoja no llegara a cargar la
   frase se lea igual ("W y S para…") en vez de salir toda pegada. */
.bloques__ayuda {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .3rem;
    margin: 1.1rem 0 0;
    font-size: .74rem;
    line-height: 1.5;
    color: var(--tinta-suave);       /* 5,6:1 sobre el blanco de la ficha */
}
.bloques__ayuda kbd {
    display: inline-block;
    padding: .05rem .3rem;
    font-family: inherit;
    font-size: .7rem;
    color: var(--tinta);
    background: var(--papel-frio);
    border: 1px solid var(--borde);
    border-bottom-width: 2px;
    border-radius: 3px;
}

/* --- Línea de tiempo (grupos con "presentacion": "cronologia") -------------
   Version ESTATICA del recurso `_plantillas/interacciones/linea-de-tiempo`:
   mismo lenguaje visual (eje vertical, nodos redondos, la fecha como dato
   aparte), pero sin desplegables, sin barra de progreso y sin Font Awesome —
   esta web no carga ningun CDN a proposito. Alli recorrer los hitos es parte
   de la actividad del curso; aqui nadie viene a hacer una actividad.

   La geometria va en variables para que el eje, el nodo y la sangria no se
   descuadren nunca entre si (mismo criterio que la plantilla):
   --eje = del borde izquierdo al CENTRO de la linea. */
.cronologia {
    --eje: .5rem;
    --nodo: .8rem;
    position: relative;
    margin: 1rem 0 0;
    padding: 0 0 0 1.85rem;
    list-style: none;                /* es un <ol>: sin esto salen los "1." "2." */
}
/* La linea del eje. Empieza y acaba en el centro del primer y del ultimo nodo,
   no en los bordes de la lista: una linea que sobresale por arriba parece que
   viene de algo anterior que no esta. */
.cronologia::before {
    content: "";
    position: absolute;
    top: .45rem;
    bottom: .45rem;
    left: calc(var(--eje) - 1px);
    width: 2px;
    background: var(--borde);
}

.cronologia__hito {
    position: relative;
    margin-bottom: .9rem;
}
.cronologia__hito:last-child { margin-bottom: 0; }

.cronologia__nodo {
    position: absolute;
    top: .3rem;
    left: calc(-1.85rem + var(--eje) - (var(--nodo) / 2));
    width: var(--nodo);
    height: var(--nodo);
    border-radius: 50%;
    background: var(--papel);
    border: 2px solid var(--primary-dark);
}
/* El hito en curso va relleno. NO es la unica señal de que lo esta: su fecha lo
   dice con todas las letras ("Activo"), asi que quien no distinga el relleno no
   se pierde nada. */
.cronologia__hito--actual .cronologia__nodo {
    background: var(--primary-dark);
    box-shadow: 0 0 0 3px rgba(39, 105, 171, .18);
}

/* La fecha va ARRIBA y pequeña, no en una columna a la izquierda como en la
   plantilla: aqui la ficha mide 30rem y una columna fija de fechas dejaba los
   titulos partidos en tres lineas. */
.cronologia__fecha {
    margin: 0;
    font-size: .78rem;
    font-weight: 600;
    letter-spacing: .02em;
    text-transform: uppercase;
    color: var(--primary-dark);      /* 5,68:1 sobre el blanco de la ficha */
}
.cronologia__titulo {
    margin: .1rem 0 .05rem;
    font-size: .97rem;
    line-height: 1.35;
    color: var(--tinta);
}
.cronologia__lugar {
    margin: 0;
    font-size: .85rem;
    color: var(--tinta-suave);       /* 5,6:1 sobre blanco */
}

/* --- Modo texto ----------------------------------------------------------- */
.cv-texto {
    flex: 1 1 auto;
    overflow: auto;
    padding: 2rem 1.25rem 4rem;
    background: var(--fondo);
}
.cv-texto[hidden] { display: none; }

#cv-texto-cuerpo {
    max-width: 46rem;
    margin: 0 auto;
}
#cv-texto-cuerpo h2 {
    margin: 2.5rem 0 .5rem;
    padding-bottom: .35rem;
    font-size: 1.35rem;
    color: var(--primary-dark);
    border-bottom: 2px solid var(--borde);
}
#cv-texto-cuerpo h3 {
    margin: 1.5rem 0 .25rem;
    font-size: 1.05rem;
}
/* Las piezas de un grupo. Aqui NO van plegadas (ver contenido.js): se marcan
   con una linea a la izquierda para que se lea que cuelgan de la entrada de
   arriba y no que son entradas hermanas. */
#cv-texto-cuerpo .bloque {
    margin-left: .1rem;
    padding-left: 1rem;
    border-left: 2px solid var(--borde);
}
#cv-texto-cuerpo h4 {
    margin: 1.1rem 0 .25rem;
    font-size: .98rem;
}
#cv-texto-cuerpo .fecha {
    display: block;
    font-size: .85rem;
    color: var(--tinta-suave);   /* 5,6:1 sobre --fondo, medido sobre ESE fondo */
}

/* --- Acceso al editor ----------------------------------------------------- */
.campo {
    width: 100%;
    padding: .5rem .7rem;
    font: inherit;
    color: var(--tinta);
    background: var(--papel);
    border: 1px solid var(--borde);
    border-radius: 6px;
}
.error {
    color: #a3261a;              /* 6,1:1 sobre el blanco del dialogo */
    font-weight: 600;
}
.acceso__botones {
    display: flex;
    gap: .5rem;
    margin-bottom: 0;
}

/* Enlace de edicion: existe, pero no compite con el CV. */
.pie__editar {
    margin-left: .5rem;
    padding: 0;
    font: inherit;
    font-size: inherit;
    color: var(--tinta-suave);
    background: none;
    border: none;
    border-bottom: 1px dotted var(--tinta-suave);
    cursor: pointer;
}
.pie__editar:hover { color: var(--primary-dark); }

/* --- Pie ------------------------------------------------------------------ */
.pie {
    padding: .6rem 1.25rem calc(.6rem + env(safe-area-inset-bottom));
    font-size: .78rem;
    color: var(--tinta-suave);
    background: var(--papel);
    border-top: 1px solid var(--borde);
    text-align: center;
}
.pie a { color: var(--primary-dark); }
.pie__creditos { margin: 0; }

/* --- Pantallas estrechas -------------------------------------------------- */
@media (max-width: 640px) {
    .cabecera { padding: .6rem .9rem; }
    .modo { padding: .45rem .7rem; font-size: .82rem; }
    /* La ayuda de teclado no pinta nada en un movil: ahi esta el mando. */
    .ayuda { display: none; }
    .pista { bottom: 11rem; }
}

/* Sin la linea de ayuda, la radio recupera la esquina de abajo... pero SOLO con
   raton.
   ---------------------------------------------------------------------------
   OJO, que aqui hubo un fallo de verdad (11-08-2026): esto era un
   `.radio { bottom: 1rem }` suelto dentro del bloque de arriba. Como va DESPUES
   del bloque de puntero grueso, le ganaba el `bottom: auto` — y con `top: 1rem`
   y `bottom: 1rem` a la vez, la radio dejaba de medir lo que ocupa y se estiraba
   660 px, media pantalla. No se veia (el fondo iba en los hijos, no en el
   contenedor), pero con su `z-index: 3` se comia los toques de todo lo que
   tuviera debajo: en el movil, el boton "Mirar" no respondia y no habia forma
   de saber por que.

   Leccion: un contenedor con `top` y `bottom` a la vez NO se ajusta a su
   contenido, se estira. Y un elemento estirado e invisible sigue recibiendo
   toques. */
@media (max-width: 640px) and (pointer: fine) {
    .radio { bottom: 1rem; }
}

/* El par "esto solo con raton / esto solo con el dedo". Se decide por el
   PUNTERO y no por el ancho: hay tabletas anchas sin teclado.
   Nacio para los consejos que mencionan una tecla (con el dedo no hay tecla que
   pulsar, y prometer un atajo que no existe es peor que no decir nada) y para
   el boton de cerrar la ficha, que solo aparece donde no hay teclado. Ahora lo
   usan tambien los creditos del pie, que con el dedo dicen menos. */
.solo-tactil { display: none; }
@media (pointer: coarse) {
    .solo-teclado { display: none; }
    .solo-tactil { display: block; }
}

/* Variante EN LINEA de `.solo-tactil`, para trozos sueltos dentro de un
   parrafo: con `display: block` el trozo se iria a su propia linea en mitad de
   la frase. (La pareja no hace falta: `.solo-teclado` solo declara el
   `display: none`, asi que cuando se ve conserva el que le toque — inline en un
   <span>.) */
.solo-tactil--linea { display: none; }
@media (pointer: coarse) {
    .solo-tactil--linea { display: inline; }
}

/* --- La franja de abajo con el dedo: creditos + radio en una linea ----------
   Con raton la radio flota en una esquina del escenario y el pie solo lleva los
   creditos, centrados. Con el dedo se juntan (Ivan, 12-08-2026): la radio
   flotando arriba tapaba habitacion, y aqui abajo hay una franja que ya existe.
   Quien mueve la radio hasta aqui es colocarRadio() en src/main.js — .oficina
   recorta con overflow:hidden y desde dentro no se puede asomar.

   OJO: las reglas de la radio van escritas como `.pie .radio`, no como
   `.radio`. Si se escriben sueltas y el JS no llega a mover el nodo, la radio se
   queda DENTRO del escenario con `position: static` y aparece incrustada en una
   esquina del canvas. Es la misma familia de fallo que el `bottom: 1rem` suelto
   de aqui arriba: una regla de posicion que se aplica donde no toca. */
@media (pointer: coarse) {
    .pie {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: .55rem;
        padding: .35rem .6rem calc(.35rem + env(safe-area-inset-bottom));
        text-align: left;
    }
    /* Los creditos ceden el sitio: son lo unico de la franja que puede
       encogerse sin dejar de cumplir su unico cometido, que es nombrar y
       enlazar al autor del arte. */
    .pie__creditos {
        flex: 1 1 auto;
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        font-size: .68rem;
        opacity: .85;
    }
    .pie .radio {
        position: static;
        flex: 0 1 auto;
        min-width: 0;
        max-width: none;
        /* La sombra dura estaba pensada para separarla del escenario oscuro;
           sobre el blanco del pie solo la hace pesada. */
        box-shadow: none;
    }
    /* Y aqui SI puede encogerse el nombre del tema: en el escenario tenia sitio
       de sobra, en una franja de movil compite con los creditos. */
    .pie .radio__titulo { font-size: .68rem; }
}

/* En un movil de verdad (no una tableta) el ancho es el enemigo, asi que la
   radio se aprieta a lo ANCHO y no a lo alto: los botones se estrechan pero
   conservan ~38 px de alto, que es lo que se acierta con el dedo. Medido en un
   iPhone de 390 px: creditos 188 px + radio 177 px, sin desbordar.
   El primero que sobra es el disco, que es decorativo. */
@media (pointer: coarse) and (max-width: 430px) {
    .pie { gap: .4rem; }
    .pie .radio { gap: .3rem; padding: .3rem .35rem; }
    .pie .radio__boton { min-width: 2rem; padding: .5rem .25rem; }
    .pie .radio__volumen { width: 2.6rem; height: 1.4rem; }
    .pie .radio__disco { display: none; }
}

/* --- Menos movimiento ----------------------------------------------------- */
/* Quien lo pide no quiere camara con inercia ni respiracion del personaje. Lo
   aplica tambien el JS (main.js lee la misma media query). */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        transition-duration: .01ms !important;
    }
}
