/* â”€â”€ BIS Cloud portal â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
   Replaces the TSplus tile list, whose appearance is whatever AppControl.ini
   allows. This is an ordinary page, so it can look like the applications it
   launches.

   The same blue the four web applications use for their side panels, so the
   portal and what it opens read as one product. See feedback_unified_look:
   the values are duplicated per application on purpose, not shared.
   -------------------------------------------------------------------------- */
/* Light is the default because the four applications default to light, and a
   portal that opened dark tabs into a light shell - or the reverse - would read
   as a bug rather than as one product. The viewer decides which one is applied
   and tells every page, portal and tabs alike; see ViewerForm.ApplyTheme.

   No colour here is new. Light takes the applications' own light chrome
   (#1f5f9e side panel, #f7f7f7 page); dark keeps exactly what this portal
   already used. */
/* LOS CUATRO COMPARTIDOS SE TOMAN DE bis-tokens.css, no se repiten aquí.

   El portal los llamaba `--bg`, `--bar`, `--text` y `--muted` mientras las otras
   tres los llamaban `--bis-bg`, `--bis-bar-bg`, `--bis-fg` y `--bis-muted`:
   nombres distintos para la misma cosa, así que nada obligaba a que coincidieran
   — y no coincidían. El portal tenía el fondo oscuro en #0f1c2b y el texto en
   #eaf1f8 mientras las otras usaban #1b1f24 y #e4e6eb.

   Se conservan los nombres locales para no reescribir las cuarenta referencias
   de esta hoja, pero ahora SON el token compartido. Cambiar el valor en un solo
   sitio cambia las cuatro aplicaciones, que es lo que faltaba. */
:root {
    --bg: var(--bis-bg);
    --bar: var(--bis-bar-bg);
    --text: var(--bis-fg);
    --muted: var(--bis-muted);

    --tile: #1f5f9e;
    --tile-hover: #2a76c0;
    /* Separate from --text on purpose. The bar and the tiles are blue in BOTH
       themes, so their text has to stay light even when the page behind them
       goes white - .tile used `color: inherit`, which would have put #1f2933 on
       a #1f5f9e tile. */
    --tile-text: #eaf1f8;
    /* El campo del formulario de acceso, OPACO. Es exactamente el mismo color
       que daba `rgba(255,255,255,.1)` sobre --bar, calculado en vez de mezclado.
       Tiene que ser opaco porque la sombra interior que tapa el autocompletado
       de Chrome no puede ser translucida: dejaria pasar el fondo del navegador,
       que es justo lo que se quiere ocultar. */
    --login-field: #356fa8;
}

/* En oscuro sólo cambian los tokens PROPIOS del portal. Los cuatro compartidos
   ya cambian solos, porque son `var(--bis-*)` y bis-tokens.css los redefine.

   Aquí estaban #0f1c2b y #eaf1f8, que no coincidían con el #1b1f24 y #e4e6eb de
   las otras tres — la diferencia de fondo y de texto que Ricardo veía al saltar
   del portal a una aplicación. Ya no existen como valores: existen una vez. */
:root[data-theme="dark"] {
    --tile: #1f5f9e;
    --tile-hover: #2a76c0;
    --tile-text: #eaf1f8;
    /* Mismo calculo sobre el --bar oscuro (#12263a). */
    --login-field: #2a3c4e;
}

* { box-sizing: border-box; }

html, body {
    margin: 0;
    height: 100%;
    /* Column flex, so main takes exactly what the header leaves. A
       `min-height: calc(100vh - 3.25rem)` needs the header's height as a magic
       number, and when that number was slightly wrong the page grew a scrollbar
       on a window that had room to spare. */
    display: flex;
    flex-direction: column;
    background: var(--bg);
    color: var(--text);
    /* La MISMA pila que Analytics, el Manager y el Timecard. Antes anteponia
       'Segoe UI', asi que el portal se veia con otra letra que las baldosas a
       las que lleva. Punto 6 de la lista de Ricardo (2026-08-27).
       El valor vive en bis-tokens.css; aqui solo se referencia. */
    font-family: var(--bis-font);
    /* No selection, no dragging: this is a menu, not a document. */
    user-select: none;
    -webkit-user-drag: none;
}

.bar {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: .75rem 1.25rem;
    background: var(--bar);
    /* Blue in both themes, so the bar's text follows the tiles rather than the
       page behind it. */
    color: var(--tile-text);
}

.brand { font-size: 1.15rem; font-weight: 300; letter-spacing: .02em; }
.brand span { font-weight: 700; }

/* margin-left:auto moved here from .quit - it is what pushes the whole control
   group to the right, and it has to sit on the FIRST of them or the theme
   button stays next to the brand. */
.theme {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-left: auto;
    background: transparent;
    border: 0;
    border-radius: 5px;
    color: inherit;
    cursor: pointer;
    padding: .45rem;
    line-height: 0;
}
.theme:hover { background: rgba(255, 255, 255, .14); }
.theme:focus-visible { outline: 2px solid rgba(255, 255, 255, .7); outline-offset: 2px; }

/* A class, not the `hidden` attribute: `hidden` is an HTML attribute and does
   nothing to an <svg>, which left both glyphs showing at once in the Timecard
   until it was rendered and looked at. */
.ico-off { display: none; }

.quit {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    background: transparent;
    border: 1px solid rgba(255, 255, 255, .45);
    border-radius: 5px;
    color: inherit;
    cursor: pointer;
    font: inherit;
    font-size: .85rem;
    line-height: 1;
    padding: .45rem .75rem;
}
/* .quit se usa en un <button> y en un <a>. El ancla trae subrayado y color de
   enlace por omision, asi que sin esto los dos controles de la barra —que estan
   uno al lado del otro— no se verian iguales. */
a.quit, a.quit:visited { text-decoration: none; color: inherit; }

.quit:hover { background: rgba(255, 255, 255, .14); }
.quit:focus-visible { outline: 2px solid rgba(255, 255, 255, .7); outline-offset: 2px; }

/* â”€â”€ Quien esta dentro â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
   El portal del visor no lo necesitaba: alli la sesion es la de Windows y no
   hay forma de estar dentro como otra persona. Aqui si, y en un equipo
   compartido saber con que cuenta se entro es la diferencia entre fichar tus
   horas y las de alguien mas.

   min-width:0 sobre un hijo de flex NO es decorativo: sin el, el ancho minimo
   de un elemento flexible es el de su contenido, asi que un nombre largo empuja
   los botones fuera de la barra en vez de recortarse. Ya paso en la barra del
   Timecard. */
.who {
    min-width: 0;
    margin-left: 1rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: .85rem;
    opacity: .85;
}

/* Un formulario en una barra flex se comporta como cualquier otro hijo y mete
   sus propios margenes. Se neutraliza para que el boton quede donde quedaria si
   no estuviera envuelto. */
.quit-form { margin: 0; display: inline-flex; }

/* El .theme lleva margin-left:auto y empuja al grupo de la derecha. Cuando el
   nombre esta presente, es EL quien tiene que absorber el espacio sobrante, o
   el nombre se pega al boton de tema y se separa del de salir. */
.who + .quit-form { margin-left: .75rem; }

/* Enlaces con aspecto de baldosa: quitarles el subrayado y el color de enlace.
   La baldosa es <a> y no <button> porque es una navegacion â€” asi funciona con
   el teclado, con el clic central y con "abrir en pestana nueva" sin escribir
   una linea de JavaScript. */
a.tile { text-decoration: none; }
a.tile:visited { color: var(--tile-text); }

/* A grid, NOT a flex container.
   As a flex parent this centred the tiles by shrinking the grid to its content -
   a flex item is `flex: 0 1 auto` - so there was no width to divide and auto-fit
   produced ONE column on a 1700px screen. A grid item stretches across the
   inline axis by default, so `align-content: center` buys vertical centring
   without costing the width. */
main {
    flex: 1;
    display: grid;
    align-content: center;
    padding: 2rem 1.5rem;
    overflow: auto;
}

.grid {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    /* Tracks with a fixed ceiling, centred by justify-content - NOT by
       `margin: 0 auto` or a max-width.
       Auto margins and justify-self make a grid or flex item shrink to its
       content, which is what produced one column inside a flex parent and then,
       after that was fixed, tiles stuck at their 150px minimum. Centring the
       TRACKS inside a full-width grid has no such effect.
       auto-fit still reflows: five across on a desktop, fewer as the window
       narrows, one on a phone. */
    /* 205, not 190: the icon grew to 101px and left only 13px of slack inside a
       190px tile - about six pixels above and below the content, which reads as
       cramped. Five of these plus their gaps come to 1121px, so they still sit
       on one row on a 1280px window. */
    grid-template-columns: repeat(auto-fit, minmax(150px, 205px));
    justify-content: center;
    gap: 1.5rem;
}

.tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .9rem;
    width: 100%;
    aspect-ratio: 1 / 1;
    padding: 1rem;
    background: var(--tile);
    border: 0;
    border-radius: 14px;
    /* Not `inherit`: the page text goes dark in light mode and the tile does
       not. */
    color: var(--tile-text);
    cursor: pointer;
    font: inherit;
    text-align: center;
    transition: transform .12s ease, background-color .12s ease;
}
.tile:hover, .tile:focus-visible {
    background: var(--tile-hover);
    transform: translateY(-3px);
    outline: none;
}
.tile:focus-visible { box-shadow: 0 0 0 3px rgba(255, 255, 255, .55); }
.tile:active { transform: translateY(0); }

/* The icon is optional. Until the artwork exists the tile shows its name alone,
   which is a working portal rather than a grid of broken-image glyphs - see
   portal.js, which removes an image that fails to load. */
/* 101px: 84 plus 20%. The source art is 256, so it is still being scaled down
   and stays sharp on a high-DPI screen. */
.tile img {
    width: 101px;
    height: 101px;
    object-fit: contain;
    pointer-events: none;
}

/* 1.175rem = 18.8px, two pixels up from 1.05rem/16.8px at the default root
   size. Kept in rem rather than px so it still follows a browser or Windows
   text-size setting. */
.tile-name { font-size: 1.175rem; font-weight: 600; }

.empty { color: var(--muted); text-align: center; }

/* â”€â”€ Pagina de inicio de sesion â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
   Anexo a la hoja que ya usa el mosaico, para que el portal web y el mosaico
   del visor sean la misma cosa visualmente mientras convivan. Usa los mismos
   tokens: si cambian ahi, cambian aqui. */
.login-card {
    width: min(23rem, 100%);
    /* justify-self, no margin auto: la tarjeta es hijo de un grid (main), y
       margin:auto sobre un hijo de flex o grid lo ENCOGE a su contenido â€” la
       misma trampa que dio una sola columna en el mosaico del visor. */
    justify-self: center;
    background: var(--bar);
    color: var(--tile-text);
    border-radius: 14px;
    padding: 2rem 1.75rem;
    box-shadow: 0 12px 40px rgba(0, 0, 0, .35);
}

.login-card .brand { font-size: 1.35rem; margin-bottom: 1.5rem; text-align: center; }

.login-label {
    display: block;
    font-size: .8rem;
    letter-spacing: .02em;
    margin: 0 0 .3rem;
    opacity: .85;
}

.login-input {
    width: 100%;
    box-sizing: border-box;
    padding: .6rem .7rem;
    margin-bottom: 1rem;
    border: 1px solid rgba(255, 255, 255, .35);
    border-radius: 6px;
    /* Opaco a proposito, no `rgba(255,255,255,.1)`. Es el mismo color en
       pantalla, pero al ser el MISMO token que usa la sombra del autocompletado,
       un campo autocompletado y uno vacio no pueden verse distintos. */
    background: var(--login-field);
    color: inherit;
    font: inherit;
}
.login-input:focus {
    outline: 2px solid rgba(255, 255, 255, .7);
    outline-offset: 1px;
}

/* â”€â”€ Cuando Chrome autocompleta â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
   Chrome pinta los campos autocompletados con SU fondo casi blanco y SU texto
   oscuro, y lo hace con !important en la hoja del agente de usuario: poner
   `background` aqui no sirve de nada. La unica via es taparlo con una sombra
   interior lo bastante grande como para cubrir el campo entero.

   Esto no era cosmetico. La tarjeta es azul con texto claro, asi que el fondo
   blanco del autocompletado dejaba el ojo -que hereda ese color claro- CLARO
   SOBRE CLARO: invisible. Se veia en la pantalla de Ricardo, con los campos
   autocompletados, y no en una recien cargada. Un defecto que solo aparece en
   la segunda visita, que es la que hace todo el mundo.

   Las dos reglas van SEPARADAS y no en una lista con coma. Una lista de
   selectores se invalida ENTERA si el navegador no entiende uno solo de sus
   miembros, asi que juntarlas haria que el navegador que soporta `:autofill`
   pero no `:-webkit-autofill` -o al reves- se quedara sin ninguna de las dos. */
.login-input:-webkit-autofill,
.login-input:-webkit-autofill:hover,
.login-input:-webkit-autofill:focus {
    -webkit-text-fill-color: var(--tile-text);
    -webkit-box-shadow: 0 0 0 100px var(--login-field) inset;
            box-shadow: 0 0 0 100px var(--login-field) inset;
    caret-color: var(--tile-text);
}
.login-input:autofill,
.login-input:autofill:hover,
.login-input:autofill:focus {
    -webkit-text-fill-color: var(--tile-text);
    -webkit-box-shadow: 0 0 0 100px var(--login-field) inset;
            box-shadow: 0 0 0 100px var(--login-field) inset;
    caret-color: var(--tile-text);
}

/* El ojo para ver la contrasena. Mismos nombres de clase que Manager y
   Analytics, para que las cuatro aplicaciones se vean iguales. */
.bis-pass-field {
    position: relative;
    display: block;
    /* EL MARGEN SE MUEVE DEL CAMPO AL CONTENEDOR, y no es un capricho.
       .login-input lleva margin-bottom:1rem. Si ese margen se queda dentro, la
       altura del contenedor es "campo + margen" y centrar el icono al 50% lo
       deja medio margen demasiado abajo. Peor: que el margen se colapse fuera
       del contenedor o no depende de reglas sutiles, asi que la posicion seria
       distinta segun el contexto. Sacando el margen, la altura del contenedor
       ES la del campo y el centrado es exacto siempre. */
    margin-bottom: 1rem;
}

.bis-pass-field .login-input {
    margin-bottom: 0;
    /* Sitio para el boton, o el texto escrito pasa por debajo del icono. */
    padding-right: 2.4rem;
}

.bis-eye {
    position: absolute;
    right: .5rem;
    top: 50%;
    transform: translateY(-50%);
    height: 26px;
    width: 26px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: none;
    /* Hereda el color del panel, que es claro sobre azul. Fijar un gris aqui
       -como en Manager, que va sobre blanco- lo dejaria casi invisible. */
    color: inherit;
    opacity: .75;
    cursor: pointer;
    padding: 0;
}

.bis-eye:hover { opacity: 1; }

.bis-eye:focus-visible {
    outline: 2px solid rgba(255, 255, 255, .7);
    outline-offset: 1px;
    opacity: 1;
}

/* Se oculta con una CLASE, no con el atributo hidden: estos son elementos SVG y
   hidden es un atributo HTML que no les hace nada. Poner hidden dejaba los dos
   iconos encima el uno del otro, y ya paso una vez en el Timecard. */
.bis-ico-off { display: none; }

.login-check {
    display: flex;
    align-items: center;
    gap: .45rem;
    font-size: .85rem;
    margin-bottom: 1.25rem;
    cursor: pointer;
}

/* Claro sobre la tarjeta azul, NO azul sobre azul.
   La primera version usaba background:var(--tile) sobre una tarjeta
   background:var(--bar), y en tema CLARO esos dos tokens valen el mismo
   #1f5f9e: el boton principal quedaba sin borde visible y solo se leia el texto
   flotando. En oscuro no se notaba -ahi --bar es #12263a- y claro es el tema por
   omision, asi que el defecto estaba justo en la vista que ve todo el mundo.
   Se vio en una captura, no leyendo el CSS: dos tokens con nombres distintos no
   parecen el mismo color hasta que se pintan.
   Los dos valores salen del juego que ya existe, sin inventar un tono nuevo. */
.login-button {
    width: 100%;
    padding: .65rem;
    border: 0;
    border-radius: 6px;
    background: var(--tile-text);
    color: var(--bar);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}
.login-button:hover { background: #fff; }
.login-button:focus-visible {
    outline: 2px solid rgba(255, 255, 255, .85);
    outline-offset: 2px;
}

/* â”€â”€ Pagina de error â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
   Reutiliza .login-card: es la misma tarjeta centrada, y darle una propia seria
   un segundo juego de valores que se desincroniza del primero. */
.error-title { font-size: 1.3rem; font-weight: 600; margin: 0 0 .75rem; }
.error-text  { margin: 0 0 1.25rem; font-size: .92rem; line-height: 1.5; opacity: .9; }

.error-code {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin: 0 0 1.5rem;
    font-size: .8rem;
    opacity: .85;
}
.error-code code {
    /* break-all, no wrap: es un identificador sin espacios, asi que sin esto
       desborda la tarjeta en un telefono en vez de partirse. */
    word-break: break-all;
    background: rgba(255, 255, 255, .12);
    border-radius: 4px;
    padding: .15rem .4rem;
}

/* .login-button es un <button> en el formulario y un <a> aqui; un ancla no
   hereda ni el centrado del texto ni la caja de bloque. */
.error-back {
    display: block;
    box-sizing: border-box;
    text-align: center;
    text-decoration: none;
}

/* Rojo destructivo del sistema de diseÃ±o, sobre fondo claro para que se lea
   encima del azul de la tarjeta. */
.login-error {
    background: #c0392b;
    color: #fff;
    padding: .6rem .7rem;
    border-radius: 6px;
    margin: 0 0 1rem;
    font-size: .88rem;
    line-height: 1.4;
}

/* Texto explicativo dentro de la tarjeta: qué hacer, o qué hacer si algo salió
   mal. Lo usan las pantallas del segundo factor.

   Hereda el color de la tarjeta y sólo baja la opacidad, en vez de fijar un gris:
   la tarjeta es azul en los DOS temas, así que un gris elegido para el claro se
   perdería en el oscuro — y al revés. Ya pasó con --tile-text. */
/* El aviso de que el segundo factor es OBLIGATORIO. No usa .login-error -rojo
   sobre blanco-: no ha fallado nada, es una instruccion. Y no usa .login-hint,
   que va al 85 % de opacidad y se lee como una nota al pie: esto es el motivo
   por el que la persona esta en esta pantalla y tiene que destacar.

   Un solo color para los dos temas a proposito: es un ambar que se lee tanto
   sobre el fondo claro como sobre el oscuro del portal, asi que no depende de
   tokens que aqui no existen. */
/* Separadores entre las operaciones de la pantalla de seguridad. Muy tenues: son
   una pausa, no una division — todo lo de esta tarjeta es la misma cuenta. */
.setup-sep {
    border: 0;
    border-top: 1px solid currentColor;
    opacity: .18;
    margin: 1.4rem 0 1.1rem;
}

.login-required {
    border-left: 3px solid #e0a800;
    padding: .55rem .7rem;
    margin: 0 0 1rem;
    font-size: .88rem;
}

.login-hint {
    color: inherit;
    opacity: .85;
    font-size: .85rem;
    line-height: 1.45;
    margin: 0 0 1rem;
}

/* El último de la tarjeta no debe empujar el borde inferior. */
.login-card > .login-hint:last-child { margin-bottom: 0; }

.login-hint a { color: inherit; }

/* Confirmación. Verde del sistema, con el mismo tratamiento que .login-error
   para que las dos ocupen el mismo sitio y no salte el contenido. */
.login-ok {
    background: #1e7e45;
    color: #fff;
    padding: .6rem .7rem;
    border-radius: 6px;
    margin: 0 0 1rem;
    font-size: .88rem;
    line-height: 1.4;
}

.login-title {
    font-size: 1.05rem;
    font-weight: 600;
    margin: 0 0 1rem;
}

/* La clave del autenticador. Monoespaciada y con letras separadas: se teclea a
   mano en un teléfono, y ahí la diferencia entre 0 y O cuesta un intento
   fallido. `user-select: all` para que un clic la seleccione entera. */
/* El QR del segundo factor. FONDO BLANCO SIEMPRE, tambien en tema oscuro: un
   lector de codigos espera modulos oscuros sobre claro, y el PNG que genera
   QRCoder es transparente donde no hay modulo. Sobre el fondo oscuro del portal
   quedaria negro sobre negro y la camara no lo veria — un codigo invisible que
   parece un fallo de la aplicacion.

   El padding es la ZONA TRANQUILA que pide la especificacion: sin margen
   alrededor, muchos lectores no encuentran el codigo. */
.setup-qr {
    display: block;
    background: #ffffff;
    padding: 12px;
    border-radius: 6px;
    margin: 0 0 1rem;
    image-rendering: pixelated;   /* sin suavizado: los bordes nitidos leen mejor */
}

.setup-key {
    font-family: Consolas, 'Courier New', monospace;
    font-size: 1.05rem;
    letter-spacing: .08em;
    background: var(--login-field);
    padding: .6rem .7rem;
    border-radius: 6px;
    margin: 0 0 1rem;
    user-select: all;
    word-break: break-all;
}

/* Los códigos de recuperación. En rejilla para que se vean de un vistazo y se
   puedan copiar de golpe; monoespaciados por lo mismo que la clave. */
.recovery-codes {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(9ch, 1fr));
    gap: .35rem .75rem;
    list-style: none;
    padding: .6rem .7rem;
    margin: 0 0 1rem;
    background: var(--login-field);
    border-radius: 6px;
    font-family: Consolas, 'Courier New', monospace;
    font-size: .9rem;
    user-select: all;
}
