JARVIS/nucleo/cara/hud/style.css
sito 8e4bc8ad94 JARVIS: asistente de voz local para Linux
Nucleo propio: oye con whisper.cpp, piensa con un modelo de Ollama, habla
con Piper, y hace RAG sobre los apuntes del usuario. 100% local, sin
cuentas ni claves.

Escrito bajo una restriccion dura, 4 GB de VRAM: el cerebro y whisper
comparten tarjeta y solo caben porque estan dimensionados para ello. El
RAG usa embeddings estaticos con busqueda hibrida; la voz clonada se sirve
de una cache de frases.

Incluye instalador (install.sh), requisitos, y documentacion del stack,
del manejo de root y de las acciones. Los apuntes indexados y el diario NO
se incluyen: son privados y el .gitignore los bloquea.
2026-08-16 15:34:37 +02:00

1233 lines
35 KiB
CSS

/* Piel de JARVIS.
Paleta del icono del lanzador: #eafcff nucleo, #7fe9ff medio,
#1b9fd8 profundo, #0a1620 fondo. Ambar #ffc46b cuando responde.
Tipografia: Inter Display y Adwaita Mono vienen ya en el runtime de GNOME,
asi que no se empaqueta ninguna fuente. Para meter una propia, dejar el
.ttf o .woff2 en esta misma carpeta y descomentar el @font-face de abajo. */
/*
@font-face {
font-family: "JarvisPropia";
src: url("fuente.woff2") format("woff2");
}
*/
/* ============================== TEMAS ==============================
El boton "tema" del HUD los rota y Python guarda el elegido en tema.txt,
al lado de este fichero. El reactor del canvas lee estas mismas variables
con getComputedStyle, asi que un tema nuevo son diez lineas aqui y nada
Un solo tema, a proposito: mantener cuatro paletas que nadie mira es codigo
que envejece sin que se note. Se cambia el color aqui arriba y ya. */
:root {
/* Naranja neon sobre negro puro. El color NO es inventado: es el #ff4e00 de
la paleta de la terminal del usuario, donde ocupa seis casillas de
dieciseis. Su terminal va negro puro con letra blanca, y esto es lo mismo.
El fondo es #000 de verdad, sin degradado. Antes habia un halo radial
calido en el centro (--centro) y con el la pantalla leia marron oscuro, no
negra: el naranja no destacaba porque competia contra su propio color
apagado de fondo. Contra negro puro salta. */
--fondo: #000000;
--fondo-hondo: #000000;
--centro: #000000; /* sin halo: el fondo es negro y punto */
--acento: #ff4e00; /* el naranja de la terminal */
--detalle: #a33200;
--brillo: #ffffff; /* blanco puro, como el primer plano de su terminal */
--texto: #ff4e00;
--tenue: rgba(255, 78, 0, 0.55);
--linea: rgba(255, 78, 0, 0.28);
--rejilla: rgba(255, 78, 0, 0.055);
--r-vivo: #ff4e00; --r-hondo: #7a2500; --r-nucleo: #ffffff;
--r-habla: #ffffff; --r-habla-hondo: #a33200; --r-habla-nucleo: #ffffff;
--r-parado: #3d1c00; --r-parado-hondo: #140900; --r-parado-nucleo: #7a4a20;
--mal: #ff2d1a;
--aviso: #ffffff;
--halo: 0 0 8px rgba(255, 78, 0, 0.6);
--halo-blanco: 0 0 6px rgba(255, 255, 255, 0.5), 0 0 18px rgba(255, 78, 0, 0.8);
/* Sin fuente de rotulo aparte: se probaron dos y ninguna convencio. Los
rotulos usan la misma que el resto del panel. */
--display: "Inter Display", "Inter", system-ui, sans-serif;
--display-peso: 600;
--titulo: "Inter Display", "Inter", system-ui, sans-serif;
--cifras: "Adwaita Mono", "DejaVu Sans Mono", monospace;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
height: 100vh;
overflow: hidden;
color: var(--texto);
font-family: var(--titulo);
/* Negro puro y la rejilla naranja encima. Nada mas.
Antes habia ademas un degradado radial calido en el centro. Contra el, el
naranja no destacaba: la pantalla leia marron oscuro y el acento competia
con su propio color apagado de fondo. Quitado el degradado, el mismo
naranja salta. */
background-color: var(--fondo);
background-image:
linear-gradient(var(--rejilla) 1px, transparent 1px),
linear-gradient(90deg, var(--rejilla) 1px, transparent 1px);
background-size: 64px 64px, 64px 64px;
}
.hud {
height: 100vh;
display: grid;
grid-template-columns: minmax(180px, 240px) 1fr minmax(220px, 320px);
align-items: center;
gap: 24px;
padding: 32px 40px 96px;
}
h2 {
font-size: 11px;
font-weight: 600;
letter-spacing: 0.32em;
text-transform: uppercase;
color: var(--tenue);
margin-bottom: 18px;
padding-bottom: 8px;
border-bottom: 1px solid var(--linea);
}
/* ---------------------------------------------------------- telemetria */
.telemetria { align-self: center; }
.medidor {
display: grid;
grid-template-columns: 42px 1fr auto;
align-items: center;
gap: 10px;
margin-bottom: 14px;
}
.etiqueta {
font-size: 10px;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--tenue);
}
.barra {
height: 4px;
background: color-mix(in srgb, var(--acento) 14%, transparent);
border-radius: 2px;
overflow: hidden;
}
.barra i {
display: block;
height: 100%;
width: 0;
background: linear-gradient(90deg, var(--detalle), var(--acento));
box-shadow: 0 0 8px color-mix(in srgb, var(--acento) 60%, transparent);
transition: width 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}
.cifra {
font-family: var(--cifras);
font-size: 11px;
color: var(--texto);
min-width: 62px;
text-align: right;
font-variant-numeric: tabular-nums;
}
h2.separado { margin-top: 26px; }
/* rejilla de nucleos: un ladrillo por hilo, como el de btop */
.nucleos {
display: grid;
grid-template-columns: repeat(8, 1fr);
gap: 3px;
}
/* Cada ladrillo lleva su propio marco y su brillo: antes eran manchas planas
y a un metro de la pantalla no se distinguian unas de otras. */
.nucleos span {
height: 26px;
border-radius: 3px;
background: color-mix(in srgb, var(--acento) 8%, transparent);
border: 1px solid color-mix(in srgb, var(--acento) 32%, transparent);
position: relative;
overflow: hidden;
}
/* se llena de abajo arriba: la altura es la carga de ese hilo */
.nucleos span::after {
content: "";
position: absolute;
left: 0; right: 0; bottom: 0;
height: var(--carga, 0%);
background: linear-gradient(180deg, var(--brillo), var(--acento) 45%, var(--detalle));
box-shadow: 0 0 10px color-mix(in srgb, var(--acento) 75%, transparent);
transition: height 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}
/* linea de brillo en la cresta: marca el nivel exacto, que el degradado solo
no lo deja claro */
.nucleos span::before {
content: "";
position: absolute;
left: 0; right: 0;
bottom: var(--carga, 0%);
height: 2px;
background: var(--brillo);
box-shadow: 0 0 8px var(--brillo);
opacity: 0.9;
transition: bottom 0.5s cubic-bezier(0.22, 1, 0.36, 1);
z-index: 1;
}
/* constantes: pares etiqueta-valor, sin barra porque no son porcentajes */
.constantes {
list-style: none;
margin-top: 18px;
font-size: 11px;
}
.constantes li {
display: flex;
justify-content: space-between;
align-items: baseline;
padding: 4px 0;
border-bottom: 1px solid color-mix(in srgb, var(--acento) 8%, transparent);
}
.constantes span {
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--tenue);
font-size: 10px;
}
.constantes b {
font-family: var(--cifras);
font-weight: 500;
color: var(--texto);
font-variant-numeric: tabular-nums;
}
/* si algo se calienta, que se vea sin buscarlo */
.constantes li.alerta b { color: #ff7a6b; }
/* la barra de reparto: a donde va cada orden. Un tramo por via, ancho segun su
porcentaje (--f, de 0 a 100), en una sola linea. */
.reparto {
display: flex;
height: 8px;
margin: 14px 0 4px;
border-radius: 4px;
overflow: hidden;
background: color-mix(in srgb, var(--acento) 8%, transparent);
}
.reparto .tramo {
flex: var(--f, 0) 0 0;
transition: flex-grow 0.6s ease;
}
.reparto .cerebro { background: var(--acento); }
.reparto .catalogo { background: color-mix(in srgb, var(--acento) 45%, transparent); }
.reparto .ruido { background: #ff7a6b; }
/* los que mas comen */
.procesos { list-style: none; font-family: var(--cifras); font-size: 11px; }
.procesos { max-height: 26vh; overflow: hidden; }
.procesos li {
display: grid;
grid-template-columns: 1fr auto auto;
gap: 8px;
padding: 3px 0;
color: color-mix(in srgb, var(--texto) 70%, transparent);
border-bottom: 1px solid color-mix(in srgb, var(--acento) 7%, transparent);
}
.procesos .nombre {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--texto);
}
.procesos .pcpu { color: var(--acento); min-width: 44px; text-align: right; }
.procesos .pmem { min-width: 42px; text-align: right; }
/* el propio asistente, resaltado: es el que interesa vigilar */
.procesos li.propio .nombre { color: var(--r-habla); font-weight: 600; }
/* -------------------------------------------------------------- nucleo */
.nucleo {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
}
#reactor { width: min(52vh, 520px); height: min(52vh, 520px); }
.estado {
font-size: 12px;
font-weight: 500;
letter-spacing: 0.42em;
text-transform: uppercase;
color: var(--tenue);
transition: color 0.4s ease;
}
.estado.escuchando { color: var(--acento); }
.estado.hablando { color: var(--r-habla); }
/* ------------------------------------------------------- tareas en marcha
Una tarjetita por orden. Van en franja bajo el reactor: caben tres o cuatro
sin taparlo, y al estar todas a la misma altura se compara de un vistazo
cual lleva mas tiempo, que es la pregunta que uno se hace mirandolas.
La franja no empuja al reactor cuando aparece la primera tarjeta: tiene
altura reservada (min-height) y esta en position: absolute respecto al
nucleo. Si creciera el layout, el reactor daria un salto cada vez que se
manda algo, y el salto se nota mucho mas que las tarjetas. */
/* La franja va en el flujo, no flotando: colocada en absolute se le montaba
encima al rotulo de estado en cuanto las tarjetas pasaban de una fila, y una
segunda fila llega en cuanto mandas cuatro cosas.
El hueco de UNA fila esta siempre reservado (min-height), asi que mandar la
primera orden no mueve el reactor. Si aparece una segunda fila el centro
sube un poco, que es honesto: hay mas cosas y ocupan mas. */
.tareas {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-content: flex-start;
gap: 10px;
width: 100%;
min-height: 74px;
margin-top: 10px;
}
.tarea {
pointer-events: auto;
width: 156px;
padding: 9px 10px;
border: 1px solid color-mix(in srgb, var(--acento) 26%, transparent);
border-left: 3px solid var(--detalle);
background: color-mix(in srgb, var(--fondo-hondo) 92%, transparent);
backdrop-filter: blur(2px);
display: grid;
gap: 6px;
animation: tarea-entra 0.22s ease-out;
transition: opacity 0.5s ease, transform 0.5s ease;
}
@keyframes tarea-entra {
from { opacity: 0; transform: translateY(6px); }
to { opacity: 1; transform: none; }
}
.tarea.desvanece { opacity: 0; transform: translateY(-4px); }
.tarea .titulo {
font-size: 10px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--texto);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tarea .fila {
display: flex;
align-items: center;
justify-content: space-between;
gap: 6px;
}
.tarea .reloj {
font-family: var(--cifras);
font-size: 11px;
color: var(--tenue);
}
.tarea .giro {
display: inline-block;
width: 7px; height: 7px;
margin-right: 5px;
border-radius: 50%;
background: var(--acento);
box-shadow: 0 0 6px var(--acento);
animation: late 1.1s ease-in-out infinite;
}
@keyframes late {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.35; transform: scale(0.72); }
}
/* La ultima linea de salida. Solo una: la tarjeta informa de que va la cosa,
el detalle entero se lee en la columna de la derecha. */
.tarea .eco {
font-family: var(--cifras);
font-size: 10px;
color: color-mix(in srgb, var(--texto) 62%, transparent);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-height: 12px;
}
.tarea button {
font-family: var(--cifras);
font-size: 11px;
line-height: 1;
padding: 4px 7px;
margin-left: 4px;
color: var(--texto);
background: color-mix(in srgb, var(--acento) 8%, transparent);
border: 1px solid color-mix(in srgb, var(--acento) 34%, transparent);
cursor: pointer;
transition: color 0.15s ease, border-color 0.15s ease,
background-color 0.15s ease;
}
.tarea button:hover {
color: var(--brillo);
border-color: var(--acento);
background: color-mix(in srgb, var(--acento) 20%, transparent);
}
/* Parar, deshabilitado, tiene que leerse como "ya no hay nada que parar" y no
como un boton que no funciona. */
.tarea button:disabled {
opacity: 0.28;
cursor: default;
background: transparent;
}
/* estados: el color del borde izquierdo es todo el mensaje */
.tarea[data-estado="hecha"] { border-left-color: var(--acento); }
.tarea[data-estado="parada"] { border-left-color: var(--r-parado); }
.tarea[data-estado="fallo"] { border-left-color: var(--mal); }
.tarea[data-estado="caducada"] { border-left-color: var(--r-habla); }
.tarea[data-estado="fallo"] .eco { color: var(--mal); }
.tarea:not([data-estado="corriendo"]) .giro {
animation: none;
opacity: 0.5;
box-shadow: none;
}
.tarea[data-estado="hecha"] .giro { background: var(--acento); }
.tarea[data-estado="parada"] .giro { background: var(--r-parado); }
.tarea[data-estado="fallo"] .giro { background: var(--mal); }
.tarea[data-estado="caducada"] .giro { background: var(--r-habla); }
/* lo que ha entendido: presente pero sin robar atencion */
.dicho {
max-width: 60ch;
min-height: 1.5em;
text-align: center;
font-size: 16px;
line-height: 1.5;
color: color-mix(in srgb, var(--texto) 78%, transparent);
opacity: 0;
transition: opacity 0.5s ease;
}
.dicho.visible { opacity: 1; }
/* ------------------------------------------------------------ acciones */
.acciones {
align-self: stretch;
display: flex;
flex-direction: column;
min-height: 0; /* sin esto el hijo con scroll no encoge */
padding-top: 6vh;
padding-bottom: 12vh;
}
.acciones h2 {
display: flex;
justify-content: space-between;
align-items: center;
gap: 10px;
flex-shrink: 0;
}
.pestanas { display: flex; gap: 4px; }
.pestanas button {
font-family: var(--titulo);
font-size: 9px;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--tenue);
background: none;
border: 1px solid transparent;
border-radius: 999px;
padding: 4px 10px;
cursor: pointer;
transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.pestanas button:hover { color: var(--brillo); }
.pestanas button.activa {
color: var(--acento);
border-color: color-mix(in srgb, var(--acento) 40%, transparent);
background: color-mix(in srgb, var(--acento) 10%, transparent);
}
/* Las dos vistas ocupan lo que queda y se desplazan por dentro: antes la
columna crecia hacia abajo y lo que no cabia quedaba fuera de la pantalla,
sin forma de llegar a ello. */
.vista {
flex: 1;
min-height: 0;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: color-mix(in srgb, var(--acento) 35%, transparent) transparent;
}
.vista.oculta { display: none; }
#filtro {
width: 100%;
font-family: var(--cifras);
font-size: 11px;
color: var(--texto);
background: color-mix(in srgb, var(--acento) 7%, transparent);
border: 1px solid var(--linea);
border-radius: 6px;
padding: 7px 10px;
margin-bottom: 10px;
}
#filtro::placeholder { color: var(--tenue); }
#filtro:focus { outline: none; border-color: var(--acento); }
.grupo {
font-size: 9px;
letter-spacing: 0.24em;
text-transform: uppercase;
color: var(--tenue);
margin: 12px 0 5px;
}
.grupo:first-child { margin-top: 0; }
.catalogo-item {
display: flex;
align-items: baseline;
gap: 6px;
padding: 4px 8px;
border-left: 2px solid color-mix(in srgb, var(--acento) 25%, transparent);
border-radius: 0 4px 4px 0;
font-size: 11px;
color: color-mix(in srgb, var(--texto) 78%, transparent);
transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.catalogo-item:hover {
background: color-mix(in srgb, var(--acento) 10%, transparent);
border-left-color: var(--acento);
color: var(--brillo);
}
/* las que piden algo detras se marcan, que si no parece que no funcionan */
.catalogo-item .hueco {
font-family: var(--cifras);
font-size: 10px;
color: var(--acento);
opacity: 0.75;
}
.catalogo-item .aviso { font-size: 10px; opacity: 0.7; }
.vacio {
font-size: 11px;
color: var(--tenue);
padding: 10px 0;
}
#registro { list-style: none; display: flex; flex-direction: column; gap: 8px; }
#registro li {
border-left: 2px solid var(--detalle);
background: color-mix(in srgb, var(--acento) 5%, transparent);
padding: 8px 12px;
border-radius: 0 6px 6px 0;
animation: entra 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}
#registro li.error { border-left-color: #ff7a6b; }
#registro .herramienta {
display: block;
font-size: 10px;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--acento);
margin-bottom: 3px;
}
#registro .salida {
font-family: var(--cifras);
font-size: 11px;
line-height: 1.45;
color: color-mix(in srgb, var(--texto) 72%, transparent);
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
@keyframes entra {
from { opacity: 0; transform: translateX(14px); }
to { opacity: 1; transform: none; }
}
/* ----------------------------------------------------------- controles */
.controles {
position: fixed;
bottom: 34px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 14px;
}
/* Se usan de lejos y a veces con prisa —callarlo cuando no toca—, asi que
priman el tamaño y el contraste sobre la discrecion. */
.controles button {
display: flex;
align-items: center;
gap: 10px;
font-family: var(--titulo);
font-size: 13px;
font-weight: 500;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--texto);
background: color-mix(in srgb, var(--acento) 10%, transparent);
border: 1px solid color-mix(in srgb, var(--acento) 40%, transparent);
border-radius: 999px;
padding: 17px 34px;
white-space: nowrap;
cursor: pointer;
position: relative;
overflow: hidden;
transition: color 0.15s, border-color 0.15s, background 0.15s, transform 0.1s;
}
/* El hover levanta el boton y lo enciende: el resplandor exterior es lo que
le da el aire de panel de mando en vez de formulario web. */
/* El boton de voz lleva el nombre dentro ("voz: sharvard"), asi que necesita
ancho fijo o la barra de controles se mueve entera al rotar. */
.controles button#voz {
min-width: 156px;
justify-content: center;
}
/* Mientras la tanda esta en marcha el boton se apaga: pulsarlo dos veces
seguidas llenaria la franja de tarjetas repetidas. */
.controles button#prueba:disabled {
opacity: 0.45;
cursor: default;
}
/* Si la voz no pudo ponerse (falta el modelo), se marca en rojo y el motivo
esta en el title. Un boton que no hace nada y no lo dice es peor que uno roto. */
.controles button#voz.fallo {
color: var(--mal);
border-color: color-mix(in srgb, var(--mal) 60%, transparent);
}
.controles button:hover {
color: var(--brillo);
border-color: var(--acento);
background: color-mix(in srgb, var(--acento) 26%, transparent);
transform: translateY(-2px);
box-shadow:
0 0 0 1px color-mix(in srgb, var(--acento) 45%, transparent),
0 6px 26px color-mix(in srgb, var(--acento) 40%, transparent),
inset 0 0 18px color-mix(in srgb, var(--acento) 18%, transparent);
letter-spacing: 0.18em;
}
.controles button:hover .punto {
box-shadow: 0 0 16px var(--acento), 0 0 30px var(--acento);
}
.controles button:active { transform: translateY(0); }
/* barrido de luz al pasar por encima */
.controles button::after {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
background: linear-gradient(100deg, transparent 35%,
color-mix(in srgb, var(--brillo) 22%, transparent) 50%, transparent 65%);
transform: translateX(-120%);
transition: transform 0.6s ease;
pointer-events: none;
}
.controles button:hover::after { transform: translateX(120%); }
/* el punto dice de un vistazo si algo esta encendido, sin leer el rotulo */
.punto {
width: 9px;
height: 9px;
border-radius: 50%;
background: var(--acento);
box-shadow: 0 0 10px var(--acento);
transition: background 0.2s, box-shadow 0.2s;
}
/* apagado: el punto se queda gris y el boton se hunde */
.controles button[data-estado="off"] {
color: color-mix(in srgb, var(--texto) 45%, transparent);
background: transparent;
border-color: color-mix(in srgb, var(--acento) 18%, transparent);
}
.controles button[data-estado="off"] .punto {
background: color-mix(in srgb, var(--texto) 30%, transparent);
box-shadow: none;
}
/* PARAR es el boton importante: se usa deprisa y de lejos */
.controles button.principal { font-size: 16px; padding: 21px 52px; }
/* parado no es un detalle discreto: si no escucha, hay que verlo */
.controles button[data-accion="parar"][data-estado="off"] {
color: #ff7a6b;
border-color: color-mix(in srgb, #ff7a6b 55%, transparent);
background: color-mix(in srgb, #ff7a6b 10%, transparent);
}
.controles button[data-accion="parar"][data-estado="off"] .punto {
background: #ff7a6b;
box-shadow: 0 0 10px #ff7a6b;
}
.controles button.secundario {
font-size: 11px;
padding: 13px 22px;
color: color-mix(in srgb, var(--texto) 55%, transparent);
}
/* ============================== PARADO ==============================
Cuando no escucha, el HUD entero baja de intensidad y sale un aviso en
medio. Se veia solo por el rotulo de un boton, y a metro y medio de la
pantalla eso no se distingue de estar funcionando. */
body.parado .telemetria,
body.parado .acciones,
body.parado #reactor,
body.parado .dicho {
opacity: 0.28;
filter: grayscale(0.6);
transition: opacity 0.4s ease, filter 0.4s ease;
}
body.parado .estado {
color: #ff7a6b;
font-size: 15px;
letter-spacing: 0.5em;
}
/* franja diagonal de fondo: inconfundible y no tapa nada */
body.parado::before {
content: "";
position: fixed;
inset: 0;
pointer-events: none;
background: repeating-linear-gradient(
-45deg,
rgba(255, 122, 107, 0.05) 0 22px,
transparent 22px 44px);
}
body.parado::after {
content: "microfono cerrado";
position: fixed;
top: 46px;
left: 50%;
transform: translateX(-50%);
font-family: var(--titulo);
font-size: 12px;
letter-spacing: 0.42em;
text-transform: uppercase;
color: #ff7a6b;
border: 1px solid rgba(255, 122, 107, 0.45);
background: rgba(255, 122, 107, 0.08);
border-radius: 999px;
padding: 9px 26px;
pointer-events: none;
}
/* -------------------------------------------------- resultados de busqueda */
/* Lista numerada para elegir por voz: "abre el tercero". Los numeros son
grandes y en color para que se localicen de un vistazo. */
#registro li.resultados ol {
margin: 6px 0 0;
padding-left: 0;
list-style: none;
counter-reset: item;
}
#registro li.resultados ol li {
font-family: var(--cifras);
font-size: 11px;
line-height: 1.5;
color: color-mix(in srgb, var(--texto) 82%, transparent);
padding: 2px 0 2px 26px;
position: relative;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* el numero, en un circulo del color de acento */
#registro li.resultados ol li::before {
content: counter(item);
counter-increment: item;
position: absolute;
left: 0;
top: 2px;
width: 18px;
height: 18px;
border-radius: 50%;
background: color-mix(in srgb, var(--acento) 18%, transparent);
border: 1px solid color-mix(in srgb, var(--acento) 45%, transparent);
color: var(--acento);
text-align: center;
font-size: 10px;
line-height: 18px;
font-weight: 700;
}
/* ============================== CANDADO ==============================
Pantalla de contraseña que tapa TODO el HUD hasta desbloquear. El
asistente no escucha ni ejecuta nada por detras mientras esto esta puesto:
no es solo visual, el panel no ha arrancado la escucha. */
.candado {
position: fixed;
inset: 0;
z-index: 100;
display: grid;
place-items: center;
/* Negro opaco, no un degradado: lo que hay detras del candado no tiene que
intuirse. Con el 96 % se sigue viendo que hay un panel debajo sin poder
leer nada de el. */
background: rgba(0, 0, 0, 0.96);
backdrop-filter: blur(6px);
}
.candado.oculto { display: none; }
.candado-caja {
display: flex;
flex-direction: column;
align-items: center;
gap: 18px;
padding: 40px 48px;
border: 1px solid color-mix(in srgb, var(--acento) 30%, transparent);
border-radius: 16px;
background: color-mix(in srgb, var(--acento) 6%, transparent);
box-shadow: 0 0 60px color-mix(in srgb, var(--acento) 20%, transparent);
}
.candado-icono {
font-size: 40px;
color: var(--acento);
text-shadow: 0 0 24px var(--acento);
animation: latido 2.4s ease-in-out infinite;
}
@keyframes latido { 0%,100%{opacity:.7} 50%{opacity:1} }
.candado-msg {
margin: 0;
font-family: var(--titulo);
font-size: 13px;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--tenue);
text-align: center;
}
.candado-msg.error { color: #ff7a6b; }
#candado-clave {
width: 260px;
font-family: var(--cifras);
font-size: 15px;
text-align: center;
color: var(--texto);
background: color-mix(in srgb, var(--acento) 8%, transparent);
border: 1px solid var(--linea);
border-radius: 8px;
padding: 12px 14px;
letter-spacing: 0.3em;
}
#candado-clave:focus { outline: none; border-color: var(--acento); }
#candado-ok {
font-family: var(--titulo);
font-size: 12px;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--fondo-hondo);
background: var(--acento);
border: none;
border-radius: 999px;
padding: 12px 32px;
cursor: pointer;
transition: background 0.15s, transform 0.1s;
}
#candado-ok:hover { background: var(--brillo); }
#candado-ok:active { transform: translateY(1px); }
/* ---------------------------------------------------------- la consola
Lo que pasa AHORA, linea a linea, como en una terminal. Va en la columna
de la derecha porque es donde el ojo ya busca "que esta haciendo", y las
tarjetas del centro se quedan para el estado de un vistazo. */
.consola-barra {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin-bottom: 8px;
font-size: 10px;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--tenue);
}
#consola-estado.ocupado {
color: var(--acento);
}
/* El punto que late solo cuando hay algo corriendo: si no, es una etiqueta
quieta y no distrae. */
#consola-estado.ocupado::before {
content: "";
display: inline-block;
width: 6px; height: 6px;
margin-right: 6px;
border-radius: 50%;
background: var(--acento);
box-shadow: 0 0 6px var(--acento);
animation: late 1.1s ease-in-out infinite;
}
#consola-parar {
font-family: var(--cifras);
font-size: 10px;
padding: 3px 8px;
color: var(--texto);
background: color-mix(in srgb, var(--mal) 12%, transparent);
border: 1px solid color-mix(in srgb, var(--mal) 45%, transparent);
cursor: pointer;
transition: background-color 0.15s ease, color 0.15s ease;
}
#consola-parar:hover:not(:disabled) {
background: color-mix(in srgb, var(--mal) 28%, transparent);
color: var(--brillo);
}
/* Deshabilitado tiene que leerse como "no hay nada que cortar", no como un
boton roto. */
#consola-parar:disabled {
opacity: 0.3;
cursor: default;
}
#consola {
font-family: var(--cifras);
font-size: 10.5px;
line-height: 1.5;
color: var(--tenue);
background: color-mix(in srgb, var(--fondo-hondo) 70%, transparent);
border: 1px solid var(--linea);
border-left: 2px solid color-mix(in srgb, var(--acento) 40%, transparent);
padding: 8px 10px;
height: calc(100vh - 260px);
overflow-y: auto;
overflow-x: hidden;
white-space: pre-wrap;
word-break: break-word;
display: flex;
flex-direction: column;
gap: 1px;
}
#consola .linea { display: block; }
#consola .linea.orden { color: var(--texto); } /* la orden que se lanza */
#consola .linea.bueno { color: var(--acento); } /* termino bien */
#consola .linea.malo { color: var(--mal); } /* fallo o corte */
#consola::-webkit-scrollbar { width: 6px; }
#consola::-webkit-scrollbar-thumb {
background: color-mix(in srgb, var(--acento) 30%, transparent);
}
/* El cerebro de fuera se marca. No es decoracion: es la unica pista visible de
que lo que se diga sale de esta maquina, y un tooltip no se ve de reojo. */
.controles .secundario.fuera {
color: var(--brillo);
border-color: color-mix(in srgb, var(--aviso, #e0a030) 65%, transparent);
background: color-mix(in srgb, var(--aviso, #e0a030) 12%, transparent);
}
/* En ::before y no en ::after: el ::after de estos botones ya esta cogido por
el barrido de luz del hover, que va en position:absolute. Poner texto ahi lo
estira por todo el boton y ademas rompe el barrido.
La palabra y no una flecha: el simbolo se pierde con la fuente del panel, y
ademas "sale de la maquina" no conviene insinuarlo con un icono. */
.controles .secundario.fuera::before {
content: "fuera";
font-family: var(--cifras);
font-size: 8px;
letter-spacing: 0.18em;
margin-right: 8px;
padding: 1px 4px;
border: 1px solid color-mix(in srgb, var(--aviso, #e0a030) 55%, transparent);
color: var(--aviso, #e0a030);
vertical-align: middle;
}
/* ================== ROTULOS EN DUNE RISE Y NEON =====================
Solo donde el texto lo escribimos nosotros y no lleva tildes, ñ, ° ni %:
la fuente no los tiene (105 glifos, comprobado) y a media palabra se
notaria el salto a Inter. Las cifras y los mensajes se quedan como estaban.
El halo va con moderacion: encendido en lo que manda —los rotulos de
seccion, el estado, los botones— y apagado en las listas largas, donde
veinte lineas brillando cansan la vista y dejan de decir nada. */
h2,
.etiqueta,
.constantes li > span,
.procesos .nombre,
.controles button .rotulo,
.estado,
.pestanas button,
.grupo,
.consola-barra,
.tarea .titulo,
.candado-msg,
#candado-ok {
font-family: var(--display);
font-weight: var(--display-peso, 700);
letter-spacing: 0.12em;
}
/* Dune Rise es ancha: con el espaciado de Inter, "ENCENDIDO" se sale de la
columna. Se compensa donde el sitio es justo. */
.etiqueta,
.constantes li > span { letter-spacing: 0.06em; font-size: 9.5px; }
.procesos .nombre { letter-spacing: 0.02em; }
h2 { letter-spacing: 0.18em; }
h2 { color: var(--acento); text-shadow: var(--halo); }
.estado {
font-size: 13px;
letter-spacing: 0.34em;
text-shadow: var(--halo);
}
.estado.escuchando { color: var(--brillo); text-shadow: var(--halo-blanco); }
/* --- los hovers ------------------------------------------------------- */
/* Los botones de abajo: al pasar por encima se encienden en blanco sobre el
naranja, que es el gesto que ya usa el resto del panel para decir "esto es
lo importante ahora". */
.controles button {
transition: color 0.16s ease, border-color 0.16s ease,
background-color 0.16s ease, box-shadow 0.2s ease,
transform 0.12s ease;
}
.controles button:hover {
color: var(--brillo);
border-color: var(--acento);
background: color-mix(in srgb, var(--acento) 22%, transparent);
box-shadow:
0 0 0 1px var(--acento),
0 0 18px color-mix(in srgb, var(--acento) 55%, transparent),
inset 0 0 22px color-mix(in srgb, var(--acento) 20%, transparent);
text-shadow: var(--halo-blanco);
}
.controles button:focus-visible {
outline: 2px solid var(--brillo);
outline-offset: 3px;
}
/* Las lineas del catalogo: una barra naranja que crece por la izquierda al
pasar, en vez de cambiarles el fondo. Marca donde esta el raton sin mover
nada de sitio. */
#catalogo .catalogo-item {
position: relative;
transition: color 0.14s ease, padding-left 0.14s ease;
}
#catalogo .catalogo-item::before {
content: "";
position: absolute;
left: 0; top: 50%;
width: 0; height: 1px;
background: var(--acento);
box-shadow: var(--halo);
transform: translateY(-50%);
transition: width 0.16s ease;
}
#catalogo .catalogo-item:hover {
color: var(--brillo);
padding-left: 16px;
text-shadow: var(--halo-blanco);
}
#catalogo .catalogo-item:hover::before { width: 10px; }
/* Los procesos, igual pero mas sobrio: son datos, no botones. */
.procesos li { transition: color 0.14s ease; }
.procesos li:hover { color: var(--brillo); }
/* Las pestañas de la derecha */
.pestanas button { transition: color 0.15s ease, box-shadow 0.2s ease; }
.pestanas button:hover { color: var(--brillo); text-shadow: var(--halo); }
.pestanas button.activa { color: var(--brillo); text-shadow: var(--halo-blanco); }
/* Las barras de los medidores brillan: son la cifra que uno mira de lejos */
.barra i { box-shadow: 0 0 10px color-mix(in srgb, var(--acento) 75%, transparent); }
/* El candado: el boton de desbloquear en blanco sobre naranja al pasar */
#candado-ok { transition: background 0.16s, color 0.16s, box-shadow 0.2s; }
#candado-ok:hover {
background: var(--brillo);
color: #000000;
box-shadow: 0 0 24px color-mix(in srgb, var(--acento) 70%, transparent);
}
/* Quien no quiera animaciones, no las tiene. */
@media (prefers-reduced-motion: reduce) {
.controles button,
#catalogo .catalogo-item,
#catalogo .catalogo-item::before,
.pestanas button,
#candado-ok { transition: none; }
}
/* Lo oido pero NO atendido: apagado y con el motivo al lado.
Se enseña igualmente, y esa es la gracia. Antes se descartaba en silencio y
la pantalla se quedaba en "escuchando" para siempre: desde fuera eso es lo
mismo que un microfono estropeado. Viendolo, se distingue al instante si el
problema es que no oye, que oye mal, o que oye bien y falta llamarle. */
.dicho.desoido {
color: var(--tenue);
opacity: 0.75;
font-style: italic;
}
.dicho.desoido::after {
content: attr(data-motivo);
display: block;
margin-top: 6px;
font-family: var(--cifras);
font-size: 10px;
font-style: normal;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--acento);
text-shadow: var(--halo);
}
/* ---- la consola: que se distinga quien dice cada cosa -------------------
Con la paleta de un solo color, .orden/.bueno/.malo caian los tres en el
mismo naranja y el hilo se leia como un bloque: no se veia donde acababa lo
que dijiste tu y empezaba lo que contesto el.
Se separa por PESO y por BLANCO, que es el resalte de la casa, y no
inventando colores nuevos:
blanco en negrita lo que se dice (tuyo o suyo): los anclajes del hilo
naranja lo que contesta y las salidas
rojo con barra lo que fallo o se descarto */
#consola .linea.orden {
color: var(--brillo);
font-weight: 600;
text-shadow: var(--halo-blanco);
}
#consola .linea.bueno {
color: var(--acento);
text-shadow: var(--halo);
}
/* El rojo solo no basta contra este naranja: se le pone barra a la izquierda,
que se ve aunque los dos tonos se parezcan. */
#consola .linea.malo {
color: var(--mal);
border-left: 2px solid var(--mal);
padding-left: 6px;
margin-left: -8px;
}
/* Las lineas sin clase son salida de las tareas: acompañan, no mandan. */
#consola .linea:not(.orden):not(.bueno):not(.malo) {
color: var(--tenue);
opacity: 0.8;
}
/* Un poco de aire entre turnos, para que el hilo se lea como una conversacion
y no como un volcado. */
#consola .linea.orden { margin-top: 6px; }
#consola .linea.orden:first-child { margin-top: 0; }
/* Mientras hay una pregunta en el aire, el panel entero lo dice: lo siguiente
que se hable es la RESPUESTA, no una orden. Sin esta señal uno contesta
"informes" y no entiende por que no ha buscado nada. */
:root.preguntando .estado {
color: var(--brillo);
text-shadow: var(--halo-blanco);
animation: late 1.4s ease-in-out infinite;
}
:root.preguntando .dicho { color: var(--brillo); }