/* =======================================================
   404heaven — style.css
   Base layout = tu maqueta, sin tocar las proporciones.
   Skin = la estética del mockup (violeta / ventanas).
======================================================= */

/* =======================================================
   TOKENS

               `'::::.
               ____||_
              /      /\
             /__/\__/  \
             |" || "|  |
   ..---""``"`""))""`"`"""`---..


======================================================= */

:root{
    --bg:        #030005;
    --panel:     #0a0210;
    --panel-2:   #140627;
    --titlebar:  #1d0834;
    --border:    #7a52c8;
    --border-dim:#39205e;
    --vio:       #a375e8;
    --vio-soft:  #8258c4;
    --vio-faint: #5c3a96;
    --bright:    #cba6ff;
    --white:     #eadcff;
    --gold:      #d8b25c;
    --font:"IBM Plex Mono",monospace;
}

/* =======================================================
   ESCALA
   Todo el CSS está en rem sobre un lienzo de referencia de
   1600x900. La raíz escala con el viewport, así que los
   márgenes y el contenido mantienen la misma proporción en
   cualquier pantalla. La home (:has(.layout)) escala también
   con la altura porque no hace scroll y debe caber entera.
======================================================= */

html{ font-size:clamp(9px, 1vw, 20px); }
html:has(.layout){ font-size:clamp(9px, min(1vw, 1.78vh), 20px); }

/* =======================================================
   RESET
======================================================= */

*{
    margin:0;
    padding:0;
    box-sizing:border-box;
}

/* =======================================================
   PAGE
======================================================= */

/* el fondo vive solo en `html`: así se propaga al lienzo y el cielo de
   estrellas (z-index:-1, hijo de body) queda por encima de él y por debajo
   del contenido. Si `body` también lo pintara, taparía las estrellas. */
html,
body{
    width:100%;
    font-family:var(--font);
}
html{ background:var(--bg); }

/* Solo HOME se ancla al viewport: es la única página que no scrollea.
   El resto crece y scrollea normal. Ojo: el overflow del elemento raíz
   se propaga al viewport, así que si esto viviera en `html` a secas
   ninguna página podría scrollear. */
html:has(.layout),
html:has(.layout) body{
    height:100%;
    overflow:hidden;
}

body{
    display:flex;
    justify-content:center;
    align-items:center;
    padding:2.1875rem;
    color:var(--vio);
    font-size:.8125rem;
    line-height:1.5;
}

/* =======================================================
   STARFIELD — el cielo de fondo
   Capa fija detrás de todo. El JS la llena con glifos en
   posiciones al azar (rejilla con jitter, para que caigan
   repartidos y no en grumos) y cada estrella tintinea a su
   propio ritmo: --dur, --delay, --dim y --lit los pone el JS.
======================================================= */

.starfield{
    position:fixed;
    inset:0;
    z-index:-1;
    overflow:hidden;
    pointer-events:none;
    user-select:none;
    contain:layout paint style;
}

.starfield .star{
    position:absolute;
    color:var(--vio-faint);
    font-size:var(--size,.75rem);
    line-height:1;
    opacity:var(--dim,.4);
    animation:star-twinkle var(--dur,4s) ease-in-out var(--delay,0s) infinite;
}

/* las más grandes brillan un poco, como la luna del header */
.starfield .star--big{
    color:var(--vio-soft);
    text-shadow:0 0 .5rem rgba(150,90,240,.45);
}

@keyframes star-twinkle{
    0%,100%{ opacity:var(--dim,.4);  transform:scale(1);    }
    50%    { opacity:var(--lit,.95); transform:scale(1.25); }
}

a{ color:var(--vio); text-decoration:none; }
a:hover{ color:var(--white); }
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible{
    outline:1px solid var(--bright);
    outline-offset:1px;
}
img{ max-width:100%; display:block; }
::selection{ background:var(--border); color:#fff; }

.ascii{
    font-family:var(--font);
    white-space:pre;
    color:var(--vio-faint);
    user-select:none;
    line-height:1.25;
}
.dotrule{ border:0; border-top:1px dotted var(--border-dim); margin:.5rem 0; }
.blink{ color:var(--vio); font-size:.75rem; }
.blink:hover{ color:var(--white); }

/* =======================================================
   MAIN LAYOUT   (tu maqueta)
======================================================= */

.layout{
    width:min(75rem,100%);
    height:100%;
    display:grid;
    grid-template-columns:13.25rem 1fr;
    gap:.9375rem;
}

/* =======================================================
   SIDEBAR   (tu maqueta)
======================================================= */

.sidebar{
    display:grid;
    grid-template-rows:
        2.4fr
        1.2fr
        2.4fr
        3.25rem;   /* = alto del .footer, así guestbook y links i like
                      cierran a la misma altura */
    gap:.9375rem;
    min-height:0;
}

/* =======================================================
   MAIN   (tu maqueta)
======================================================= */

.main{
    display:grid;
    grid-template-rows:
        9.25rem
        1fr
        1fr
        5rem
        3.25rem;
    gap:.9375rem;
    min-height:0;
    min-width:0;
}

/* =======================================================
   HEADER
======================================================= */

.hero{
    display:grid;
    grid-template-columns:1fr;
    min-height:0;
}

.logo{
    display:flex;
    flex-direction:column;
    justify-content:center;
    align-items:center;
    position:relative;
    border:0;                 /* el logo no lleva ventana */
    overflow:hidden;
}

.logo-ascii{
    font-family:var(--font);
    white-space:pre;
    color:var(--vio);
    font-size:.72rem;
    line-height:1.18;
    text-shadow:0 0 12px rgba(150,90,240,.35);
    user-select:none;
}

.star-rule{
    width:100%;
    color:var(--vio-faint);
    overflow:hidden;
    white-space:nowrap;
    text-align:center;
    font-size:.75rem;
    letter-spacing:.125rem;
    user-select:none;
    margin-top:.375rem;
}

.moon{
    position:absolute;
    top:.25rem; right:5%;
    color:var(--vio);
    font-size:1.875rem;
    text-shadow:0 0 16px rgba(170,110,255,.5);
}
.sparkle{ position:absolute; color:var(--vio-faint); font-size:.75rem; }

/* =======================================================
   CONTENT ROWS   (tu maqueta)
======================================================= */

.row{
    display:grid;
    grid-template-columns:repeat(12,1fr);
    gap:.9375rem;
    min-height:0;
}

/* =======================================================
   WINDOW SIZES   (tu maqueta)
======================================================= */

/* Primera fila */
.welcome{ grid-column:1 / 5; }
.music{   grid-column:5 / 9; }
.brain{   grid-column:9 / 13; }

/* Segunda fila */
.memory{  grid-column:1 / 5; }
.journal{ grid-column:5 / 9; }
.bug{     grid-column:9 / 13; }

/* =======================================================
   SHARED STYLES — la ventana
======================================================= */

.window{
    display:flex;
    flex-direction:column;
    background:var(--panel);
    border:1px solid var(--border);
    min-height:0;
    min-width:0;
}

.win__bar{
    display:flex;
    align-items:center;
    gap:.4375rem;
    background:var(--titlebar);
    border-bottom:1px solid var(--border);
    padding:.25rem .4375rem;
    color:var(--bright);
    font-size:.75rem;
    user-select:none;
    flex-shrink:0;
}
.win__bar .icon{ color:var(--vio); width:.9375rem; text-align:center; }
.win__bar .title{ flex:1; }
.win__btns{ display:flex; gap:.25rem; }
.win__btns span{
    width:.9375rem; height:.9375rem;
    border:1px solid var(--border);
    display:grid; place-items:center;
    font-size:.5625rem;
    color:var(--vio);
    line-height:1;
}

.win__body{
    padding:.6875rem .8125rem;
    flex:1;
    min-height:0;
    overflow:hidden;
    display:flex;
    flex-direction:column;
}

/* =======================================================
   SIDEBAR — nav
======================================================= */

/* esta ventana no trae .win__body: la lista es el cuerpo. Por eso se
   estira ella misma para ocupar el alto que sobra y centra los enlaces
   ahí adentro. En las páginas internas la ventana mide lo que mide la
   lista, así que el flex:1 no cambia nada. */
.nav-list{
    list-style:none;
    padding:.3125rem 0;
    overflow:hidden;
    flex:1;
    min-height:0;
    display:flex;
    flex-direction:column;
    justify-content:center;
}
/* sin esto, cuando la ventana queda corta los enlaces se aplastan y se
   les corta el texto por dentro; preferimos que sobresalgan y listo */
.nav-list li{ flex-shrink:0; }
.nav-list a{
    display:flex;
    align-items:center;
    gap:.5625rem;
    padding:.375rem .75rem;
    color:var(--vio);
    font-size:.8125rem;
}
.nav-list a:hover{ background:var(--panel-2); color:var(--white); }
.nav-list a[aria-current="page"]{ color:var(--white); }
.nav-ico{ width:1.375rem; display:inline-flex; justify-content:center; color:var(--vio-soft); }
.nav-ico svg{ width:1rem; height:1rem; }
.nav-ico svg *{ stroke:currentColor; fill:none; stroke-width:1.4; }

/* el botón que pliega el nav lo agrega navDropdown() en main.js, pero solo
   se usa en mobile: en desktop la lista se ve siempre y el botón sobra.
   Las reglas que lo encienden están en el bloque responsive del final. */
.nav-toggle{ display:none; }

/* =======================================================
   SIDEBAR — language switch (dropdown)
   Lo agrega langSwitch() en main.js dentro del win__bar de
   "navigate", en toda página (comparten esa estructura). Fase 1:
   solo cambia journal/lab (ver Lang en main.js); la pieza queda
   acá, global, para cuando el resto de la interfaz se traduzca.
======================================================= */
.lang-switch{ position:relative; }

.lang-toggle{
    display:flex;
    align-items:center;
    gap:.3125rem;
    background:none;
    border:1px solid var(--border-dim);
    color:var(--vio-soft);
    font-family:var(--font);
    font-size:.625rem;
    letter-spacing:.03em;
    padding:.0625rem .375rem;
    line-height:1.3;
    cursor:pointer;
}
.lang-toggle:hover{ color:var(--white); border-color:var(--border); }
.lang-switch.is-open .lang-toggle{ color:var(--white); border-color:var(--vio); background:var(--panel-2); }
.lang-flag{ display:inline-flex; flex-shrink:0; }
.lang-flag svg{ width:1.0625rem; height:.71rem; display:block; }
.lang-caret{ font-size:.8125rem; line-height:1; color:var(--vio-soft); }
.lang-switch.is-open .lang-caret{ transform:rotate(180deg); }

.lang-menu{
    position:absolute;
    top:calc(100% + .3125rem);
    right:0;
    z-index:20;
    min-width:100%;
    list-style:none;
    background:var(--panel-2);
    border:1px solid var(--border);
    padding:.1875rem;
    box-shadow:0 .25rem .75rem rgba(3,0,6,.55);
}
.lang-menu[hidden]{ display:none; }

.lang-option button{
    display:flex;
    align-items:center;
    gap:.4375rem;
    width:100%;
    background:none;
    border:0;
    color:var(--vio-soft);
    font-family:var(--font);
    font-size:.625rem;
    letter-spacing:.03em;
    padding:.28125rem .4375rem;
    white-space:nowrap;
    text-align:left;
    cursor:pointer;
}
.lang-option button:hover{ background:var(--panel); color:var(--white); }
.lang-option[aria-selected="true"] button{ color:var(--white); }
.lang-option[aria-selected="true"] button::after{ content:"✓"; margin-left:auto; color:var(--vio); }

/* =======================================================
   SIDEBAR — counter
======================================================= */

.counter .win__body{ align-items:center; justify-content:center; text-align:center; }
.counter .label{ font-size:.75rem; }
.counter .digits{
    font-size:1.875rem;
    letter-spacing:.4375rem;
    color:var(--gold);
    text-shadow:0 0 10px rgba(216,178,92,.4);
    margin:.125rem 0 0 .4375rem;
}
.counter .thanks{ font-size:.72rem; margin-bottom:.5rem; }
.counter .ascii{ font-size:.75rem; color:var(--vio-soft); }

/* =======================================================
   SIDEBAR — guestbook
======================================================= */

.gb-head{ text-align:center; font-size:.78rem; }
.gb-book{ text-align:center; margin:.25rem 0 .625rem; font-size:.75rem; }
.gb-entries{ flex:1; min-height:0; overflow:hidden; }

/* marquee vertical del widget de la home. El JS clona las entradas, así
   que el track mide el doble: -50% = exactamente un set, sin costura. */
.gb-track.is-scrolling{ animation:gb-scroll var(--gb-dur,20s) linear infinite; }
.gb-track.is-scrolling:hover{ animation-play-state:paused; }
@keyframes gb-scroll{ to{ transform:translateY(-50%); } }

.gb-entry{ margin-bottom:.6875rem; font-size:.75rem; line-height:1.45; }
.gb-entry .who{ color:var(--gold); }
.gb-entry .who::before{ content:"» "; color:var(--vio-soft); }
.gb-entry .msg{ margin-left:.875rem; }
.gb-entry .when{ margin-left:.875rem; color:var(--vio-faint); }

.gb-actions{ flex-shrink:0; padding-top:.5rem; text-align:center; }
.gb-actions .blink{ display:block; margin-top:.25rem; }

.gb-form{ display:grid; gap:.4375rem; margin-top:.5rem; flex-shrink:0; }
.gb-form input,
.gb-form textarea{
    background:var(--panel-2);
    border:1px solid var(--border-dim);
    color:var(--bright);
    font-family:var(--font);
    font-size:.75rem;
    padding:.3125rem .5rem;
    width:100%;
}
.gb-form textarea{ resize:none; min-height:2.5rem; }
.gb-form button{
    justify-self:start;
    background:var(--titlebar);
    border:1px solid var(--border);
    color:var(--bright);
    font-family:var(--font);
    font-size:.75rem;
    padding:.25rem .75rem;
    cursor:pointer;
}
.gb-form button:hover{ background:var(--panel-2); }

/* =======================================================
   SIDEBAR — copyright
======================================================= */

.copyright{
    display:flex;
    justify-content:center;
    align-items:center;
    text-align:center;
    border:0;
    color:var(--vio);
    font-size:.75rem;
    padding:.25rem 0;
    line-height:1.5;
}

/* =======================================================
   WELCOME widget
======================================================= */

.welcome .win__body{
    align-items:center;
    justify-content:center;
    text-align:center;
    gap:1px;
}
.welcome .ascii{ font-size:.66rem; margin-bottom:.5rem; }
.welcome p{ font-size:.78rem; }
.welcome .heart{ color:var(--vio-soft); margin-top:.3125rem; }

/* =======================================================
   MUSIC (now playing) — reproductor real
   Dos columnas iguales: cover cuadrado (con dither) a la
   izquierda, título / artista / tiempo a la derecha. Debajo,
   a lo ancho, el volumen y los tres botones.
======================================================= */

.np-top{
    display:grid;
    grid-template-columns:1fr 1fr; /* las dos columnas miden lo mismo */
    gap:.8125rem;
    flex:1;
    min-height:0;
    align-items:center;
}

/* el cuadrado manda sobre el alto, no sobre el ancho: la home no
   scrollea, así que la ventana puede quedar más baja que ancha y un
   cover atado al ancho de la columna se saldría por abajo */
.np-cover{
    height:100%;
    aspect-ratio:1;
    max-width:100%;
    justify-self:center;
    background:#000;
    border:1px solid var(--border-dim);
    overflow:hidden;
    display:grid;
    grid-template-rows:100%;
    grid-template-columns:100%;
    place-items:center;
}
/* igual que en .mem-photo: la imagen y el canvas del dither ocupan la
   celda entera, si no el alto en % no resuelve y object-fit no hace nada */
.np-cover img,
.np-cover .dither-wrap{
    grid-area:1/1;
    align-self:stretch;
    justify-self:stretch;
    width:100%;
    height:100%;
}
.np-cover img{ object-fit:cover; object-position:center; }

.np-meta{ min-width:0; }

/* marquee horizontal: el <p> recorta y el <span> de adentro se desplaza
   sólo si el texto no entra. El JS mide y pone --np-shift / --np-dur. */
.np-line{ overflow:hidden; white-space:nowrap; min-width:0; }
.np-line > span{ display:inline-block; white-space:pre; }
.np-line.is-scrolling > span{
    animation:np-marquee var(--np-dur,12s) ease-in-out infinite;
}
/* ida, pausa, vuelta: nunca hay un salto seco al volver al principio */
@keyframes np-marquee{
    0%,12%   { transform:translateX(0); }
    46%,58%  { transform:translateX(var(--np-shift,0)); }
    92%,100% { transform:translateX(0); }
}

.np-title{ color:var(--bright); font-size:.9375rem; }
.np-artist{ color:var(--vio); font-size:.8125rem; margin-top:.125rem; }
.np-time{ font-size:.78rem; color:var(--vio-soft); margin-top:.375rem; }

.np-bottom{ flex-shrink:0; }
.np-audio{ display:none; }

.np-volume{ display:flex; align-items:center; gap:.5rem; }
.np-vol{
    -webkit-appearance:none; appearance:none;
    flex:1;
    min-width:0;
    height:.875rem;
    background:none;
    cursor:pointer;
}
.np-vol:focus-visible{ outline:1px solid var(--bright); outline-offset:2px; }
/* la parte ya recorrida se pinta con --np-fill, que actualiza el JS */
.np-vol::-webkit-slider-runnable-track{
    height:.3125rem;
    border:1px solid var(--border-dim);
    background:linear-gradient(to right,
        var(--vio) var(--np-fill,35%), var(--panel-2) var(--np-fill,35%));
}
.np-vol::-webkit-slider-thumb{
    -webkit-appearance:none;
    width:.4375rem; height:.8125rem;
    margin-top:-.3125rem;
    background:var(--bright);
    border:1px solid var(--border);
}
.np-vol::-moz-range-track{
    height:.3125rem;
    border:1px solid var(--border-dim);
    background:var(--panel-2);
}
.np-vol::-moz-range-progress{ height:.3125rem; background:var(--vio); }
.np-vol::-moz-range-thumb{
    width:.4375rem; height:.8125rem;
    border-radius:0;
    background:var(--bright);
    border:1px solid var(--border);
}

.np-controls{ display:flex; justify-content:center; gap:2.5rem; padding-top:.375rem; }
.np-mute,
.np-controls button{
    background:none; border:0;
    padding:0;
    color:var(--vio);
    font-family:var(--font);
    font-size:1rem;
    line-height:1;
    cursor:pointer;
}
.np-mute{ font-size:.875rem; min-width:2.5ch; text-align:left; }
.np-mute:hover,
.np-controls button:hover{ color:var(--white); }
.np-controls button[disabled]{ color:var(--vio-faint); cursor:default; }
.np-controls button[disabled]:hover{ color:var(--vio-faint); }

/* =======================================================
   NP-BAR — el reproductor compacto de las páginas internas
   La home usa el widget completo (.music); acá va una barra
   fija abajo de todo, que la inyecta musicPlayer() en main.js.
   Comparte ids y clases (.np-line, .np-vol, .np-mute) con el
   widget, así que hereda el marquee y el estilo del volumen.
======================================================= */

.np-bar{
    position:fixed;
    left:0; right:0; bottom:0;
    z-index:60;              /* debajo del lightbox (90), encima del resto */
    display:flex;
    align-items:center;
    gap:.75rem;
    padding:.375rem .75rem;
    background:var(--panel);
    border-top:1px solid var(--border);
}

.np-bar__cover{
    width:2.25rem; height:2.25rem;
    flex-shrink:0;
    background:#000;
    border:1px solid var(--border-dim);
    overflow:hidden;
    display:grid;
    grid-template-rows:100%;
    grid-template-columns:100%;
}
/* igual que en .np-cover: imagen y canvas ocupan la celda entera, si no el
   alto en % no resuelve y object-fit no hace nada */
.np-bar__cover img,
.np-bar__cover .dither-wrap{
    grid-area:1/1;
    width:100%; height:100%;
}
.np-bar__cover img{ object-fit:cover; object-position:center; }

/* el bloque elástico: es lo único que cede cuando la ventana se angosta */
.np-bar__meta{ flex:1; min-width:0; }
.np-bar .np-title{ color:var(--bright); font-size:.8125rem; }
.np-bar .np-artist{ color:var(--vio); font-size:.72rem; }

.np-bar__controls{ display:flex; gap:1rem; flex-shrink:0; }
.np-bar__controls button{
    background:none; border:0;
    padding:0;
    color:var(--vio);
    font-family:var(--font);
    font-size:1rem;
    line-height:1;
    cursor:pointer;
}
.np-bar__controls button:hover{ color:var(--white); }
.np-bar__controls button[disabled]{ color:var(--vio-faint); cursor:default; }
.np-bar__controls button[disabled]:hover{ color:var(--vio-faint); }

.np-bar .np-time{
    flex-shrink:0;
    font-size:.72rem;
    color:var(--vio-soft);
    font-variant-numeric:tabular-nums; /* el tiempo no baila al correr */
}

.np-bar__vol{ display:flex; align-items:center; gap:.375rem; flex-shrink:0; }
.np-bar__vol .np-vol{ width:4.5rem; flex:none; }

/* la barra tapa el final de la página: se le devuelve ese alto en padding.
   Va con :has() para que solo pase cuando la barra existe de verdad — si
   el JS no llegó a inyectarla, no queda un hueco al pie sin motivo. */
body:has(.np-bar) .page{ padding-bottom:4.5rem; }

/* pantallas angostas: se va el tiempo, que es lo único prescindible. El
   volumen se queda — en el celular es justo donde más se necesita. */
@media (max-width:600px){
    .np-bar .np-time{ display:none; }
    .np-bar__controls{ gap:1.125rem; }
    .np-bar__vol .np-vol{ width:3.25rem; }
    .np-bar{ gap:.5rem; }
}

/* =======================================================
   BRAINWORM
======================================================= */

.brain .win__body{ justify-content:center; }
.brain .ascii{ font-size:.66rem; text-align:center; margin:0 auto .5625rem; }
.bw-name{ color:var(--bright); font-size:.9375rem; margin-bottom:.25rem; }
.bw-note{ font-size:.78rem; }
.bw-since{ font-size:.78rem; margin:.125rem 0 .5rem; }

/* =======================================================
   BUG OF THE MONTH
======================================================= */

.bug-link{ display:flex; flex-direction:column; flex:1; min-height:0; }
.bug-photo{
    flex:1;
    min-height:0;
    border:1px solid var(--border-dim);
    background:#000;
    overflow:hidden;
    display:grid;
    /* la celda mide lo que mide la caja, nunca lo que mide la foto: con
       filas auto una imagen más alta que el contenedor estira la fila, se
       sale por abajo y el overflow la recorta desde arriba, dejando a
       object-fit sin nada que hacer */
    grid-template-rows:100%;
    grid-template-columns:100%;
    place-items:center;
}
/* La foto ocupa toda la celda: el place-items:center del contenedor es
   para el placeholder de texto, no para la imagen. Sin el stretch
   explícito el alto en % no resuelve y object-fit se queda sin efecto. */
.bug-photo img,
.bug-photo svg,
.bug-photo .dither-wrap{
    grid-area:1/1;
    align-self:stretch;
    justify-self:stretch;
    width:100%;
    height:100%;
}
.bug-photo img,
.bug-photo svg{ object-fit:cover; object-position:center; }
.bug-common{ color:var(--white); font-size:.875rem; margin-top:.5rem; }
.bug-species{ color:var(--gold); font-style:italic; font-size:.8125rem; margin:.0625rem 0 .375rem; }

/* home: nombre y especie en una sola línea para que la foto respire */
.bug-name{ margin-top:.5rem; text-align:center; line-height:1.35; }
.bug-name .bug-common,
.bug-name .bug-species{ display:inline; margin:0; font-size:.8125rem; }
.bug-name .bug-star{ color:var(--vio-soft); margin:0 .4375rem; }
/* mientras supabase no conteste los nombres están vacíos: el separador
   no tiene que quedar colgado solo en el medio de la nada */
.bug-name:has(.bug-common:empty) .bug-star{ display:none; }

/* =======================================================
   JOURNAL widget
======================================================= */

.jp-title{ color:var(--bright); font-size:.9375rem; }
.jp-date{ font-size:.78rem; margin-top:.0625rem; }
.jp-scroll{ flex:1; min-height:0; overflow:hidden; }
.jp-body{ font-size:.78rem; }
.jp-body p{ margin-bottom:.5625rem; }

/* =======================================================
   MEMORY widget
======================================================= */

.mem-photo{
    flex:1;
    min-height:0;
    border:1px solid var(--border-dim);
    background:#000;
    overflow:hidden;
    display:grid;
    /* misma razón que en .bug-photo: la celda la manda la caja, no la foto */
    grid-template-rows:100%;
    grid-template-columns:100%;
    place-items:center;
    color:var(--vio-faint);
    font-size:.75rem;
}
.mem-photo img,
.mem-photo .dither-wrap{
    grid-area:1/1;
    align-self:stretch;
    justify-self:stretch;
    width:100%;
    height:100%;
}
.mem-photo img{ object-fit:cover; object-position:center; }
.mem-caption{ color:var(--white); font-size:.875rem; margin-top:.4375rem; }
.mem-date{ font-size:.78rem; margin:.0625rem 0 .375rem; }

/* =======================================================
   LINKS
======================================================= */

.links .win__body{
    justify-content:center;
    padding:.375rem .8125rem;
}
.linkrow{
    display:flex;
    flex-wrap:wrap;
    gap:.75rem;
    justify-content:center;
}
.linkbtn{
    height:2.25rem;
    min-width:7.5rem;
    border:1px solid var(--border);
    background:var(--panel);
    display:inline-flex;
    align-items:center;
    justify-content:center;
    gap:.5625rem;
    padding:0 .875rem;
    color:var(--bright);
    font-size:.66rem;
    letter-spacing:.08em;
    text-transform:uppercase;
    line-height:1.25;
    text-align:center;
}
.linkbtn:hover,
.linkbtn--lit{ background:var(--panel-2); }
.linkbtn .ico{ color:var(--vio); font-size:.9375rem; }

/* =======================================================
   FOOTER
======================================================= */

.footer{
    display:flex;
    justify-content:space-between;
    align-items:center;
    gap:1rem;
    border:0;
    color:var(--vio);
    font-size:.75rem;
    padding:0 .375rem;
    flex-wrap:wrap;
    overflow:hidden;
}
.footer .fcol{ display:flex; align-items:center; gap:.75rem; }
.footer .stars{ color:var(--vio-faint); letter-spacing:.1875rem; }
.footer .robot{ font-size:.625rem; color:var(--vio-soft); }
.footer a{ color:var(--vio); }
.footer a:hover{ color:var(--white); }

/* =======================================================
   WELCOME SCREEN (front door)
======================================================= */

.welcome-screen{
    position:fixed;
    inset:0;
    z-index:100;
    background:var(--bg);
    display:grid;
    place-items:center;
    padding:1.5rem;
    transition:opacity 1.1s ease;
    overflow:auto;
}
.welcome-screen.leaving{ opacity:0; pointer-events:none; }
.welcome-screen[hidden]{ display:none; }

.ws-inner{ text-align:center; max-width:42.5rem; }
.ws-inner .logo-ascii{ font-size:.62rem; }
.ws-msg{ color:var(--bright); margin:1.375rem 0 .25rem; font-size:.875rem; }
.ws-msg-2{ color:var(--vio); font-size:.78rem; margin-bottom:1.5rem; }

.ws-enter{
    background:var(--titlebar);
    border:1px solid var(--border);
    color:var(--bright);
    font-family:var(--font);
    font-size:.875rem;
    letter-spacing:.22em;
    padding:.6875rem 2.875rem;
    cursor:pointer;
}
.ws-enter:hover{ background:var(--panel-2); color:#fff; }

.best{
    margin:1.875rem auto 0;
    max-width:17.5rem;
    text-align:left;
}
.best .win__body{ font-size:.78rem; display:block; }
.best p{ margin:.1875rem 0; }
.best p::before{ content:"> "; color:var(--vio-faint); }

/* la tele vive fuera de la ventana, debajo. width:fit-content + margin
   auto centra el bloque entero sin tocar el interior del dibujo, que se
   alinea solo con sus propios espacios. */
.ws-art{
    display:block;
    width:fit-content;
    margin:.75rem auto 0;
    font-size:.6875rem;
}

/* La puerta es un panel opaco (background:var(--bg)) por encima del cielo
   de fondo, así que el JS le mete su propia .starfield dentro. Funciona
   porque .welcome-screen crea contexto de apilado (fixed + z-index): el
   z-index:-1 de la capa queda sobre su fondo y bajo .ws-inner. */

/* =======================================================
   LIGHTBOX + DITHER
======================================================= */

/* el JS envuelve cada <img class="dither"> en .dither-wrap. El tamaño del
   wrapper dentro de .mem-photo / .bug-photo se define junto a esas fotos. */
.dither-wrap{ position:relative; cursor:pointer; }
/* las que no abren el lightbox tampoco deben prometerlo con el cursor */
.dither-wrap:has(img[data-nozoom]){ cursor:default; }
.dither-wrap canvas{
    position:absolute; inset:0;
    width:100%; height:100%;
    /* mismo recorte que la <img> de abajo: el canvas conserva el ratio de
       la foto, así que sin esto se deforma cuando no coincide con la caja
       y el hover "corrige" el estirado al descubrir la imagen real */
    object-fit:cover; object-position:center;
    transition:opacity .35s ease;
    pointer-events:none;
}
.dither-wrap:hover canvas{ opacity:0; }

.lightbox{
    position:fixed; inset:0;
    background:rgba(3,0,6,.93);
    display:none; place-items:center;
    z-index:90; padding:1.875rem;
    cursor:zoom-out;
}
.lightbox.open{ display:grid; }
.lightbox img{ max-width:92vw; max-height:88vh; border:1px solid var(--border); }

/* =======================================================
   INNER PAGES  (journal, memories, collections, lab,
   art, bug, guestbook) — scrolleables, misma piel
======================================================= */

body:has(.page){ display:block; padding:0; }

.page{
    max-width:77.5rem;
    margin:0 auto;
    padding:1.125rem;
    display:grid;
    grid-template-columns:13.25rem 1fr;
    gap:.9375rem;
    align-items:start;
}
.page .sidebar{ position:sticky; top:1.125rem; display:grid; gap:.9375rem; grid-template-rows:none; }
.content{ min-width:0; display:grid; gap:.9375rem; }
@media (max-width:900px){
    .page{ grid-template-columns:1fr; }
    .page .sidebar{ position:static; order:2; }
    .content{ order:1; }
}

/* las páginas internas usan .win con la misma anatomía */
.win{
    background:var(--panel);
    border:1px solid var(--border);
    display:flex;
    flex-direction:column;
    /* item de grilla: sin esto su ancho mínimo es el de su contenido, y
       algo que no pueda partirse (una línea de código, una URL larga)
       estira la ventana fuera de la pantalla en vez de scrollear adentro */
    min-width:0;
}
.win .win__body{ overflow:visible; display:block; flex:none; }

.page-head h1{ font-size:1.125rem; color:var(--white); font-weight:500; }
.page-head .sub{ color:var(--vio-soft); margin-top:.25rem; font-size:.78rem; }

/* =======================================================
   POST — journal y lab
   El HTML de cada post lo escribe el Studio y viene casi
   pelado: solo trae estilos inline para color, tamaño de
   fuente, alineación y ancho de imagen. TODO lo estructural
   (jerarquía, sangrías, bullets, márgenes) se define acá, o
   el post se ve como HTML crudo. Ojo con el reset de arriba:
   `*{margin:0;padding:0}` deja las listas sin viñeta y sin
   sangría, así que hay que devolvérselas a mano.
======================================================= */

.entry-win .win__body{ padding:.8125rem 1rem 1rem; }

.entry-cover{
    border:1px solid var(--border-dim);
    background:#000;
    margin:.75rem 0 1rem;
}
.entry-cover img{ width:100%; height:auto; }

.entry{
    /* los tamaños del content son em (0.8em / 1.3em / 1.8em), o sea
       relativos a esto: si esta base cambia, cambia todo el post */
    font-size:.82rem;
    line-height:1.7;
    color:var(--vio);
    /* el texto lo escribo yo y puede traer una URL larguísima: sin esto
       estira la ventana y rompe la grilla de la página */
    overflow-wrap:anywhere;
}
.entry > :first-child{ margin-top:0; }
.entry > :last-child{ margin-bottom:0; }

.entry p{ margin:0 0 .9rem; }

.entry h2,
.entry h3,
.entry h4{ color:var(--white); font-weight:500; line-height:1.35; margin:1.5rem 0 .5rem; }
.entry h2{ font-size:1.15rem; }
.entry h3{ font-size:1rem; }
.entry h4{ font-size:.9rem; color:var(--bright); }

.entry strong,
.entry b{ color:var(--white); font-weight:500; }
.entry em,
.entry i{ font-style:italic; }
.entry u{ text-underline-offset:.15em; }
.entry s{ opacity:.7; }
.entry sub,
.entry sup{ font-size:.72em; line-height:0; }

.entry a{ color:var(--bright); text-decoration:underline; text-underline-offset:.15em; }
.entry a:hover{ color:#fff; }

.entry blockquote{
    border-left:2px solid var(--border);
    background:var(--panel-2);
    padding:.5rem .9rem;
    margin:1.1rem 0;
    color:var(--vio-soft);
    font-style:italic;
}

.entry ul,
.entry ol{ margin:0 0 .9rem; padding-left:1.6rem; }
.entry ul{ list-style:disc; }
.entry ol{ list-style:decimal; }
.entry li{ margin:.2rem 0; }
.entry li::marker{ color:var(--vio-faint); }

.entry hr{
    border:0;
    border-top:1px dotted var(--border-dim);
    margin:1.4rem 0;
}

.entry code{
    background:var(--panel-2);
    border:1px solid var(--border-dim);
    padding:0 .25rem;
    color:var(--bright);
    font-size:.95em;
}
.entry pre{
    background:var(--panel-2);
    border:1px solid var(--border-dim);
    padding:.7rem .8rem;
    margin:0 0 .9rem;
    font-size:.95em;
    /* el código no se parte solo: que scrollee la caja, no la página */
    overflow-x:auto;
    white-space:pre;
}
.entry pre code{ background:none; border:0; padding:0; font-size:1em; }

/* el ancho lo trae la imagen en su style inline (width:35%, etc.): acá
   solo se centra y se le pone un techo para que no desborde en mobile */
.entry img{
    display:block;
    margin:1.2rem auto;
    max-width:100%;
    height:auto;
}

.stack{ display:grid; gap:.9375rem; }
.grid-2{ display:grid; grid-template-columns:1fr 1fr; gap:.9375rem; }
.grid-3{ display:grid; grid-template-columns:repeat(3,1fr); gap:.9375rem; }
.grid-4{ display:grid; grid-template-columns:repeat(4,1fr); gap:.9375rem; }
@media (max-width:1000px){ .grid-3,.grid-4{ grid-template-columns:repeat(2,1fr); } }
@media (max-width:640px){ .grid-2,.grid-3,.grid-4{ grid-template-columns:1fr; } }

.tag{ display:inline-block; border:1px solid var(--border-dim); color:var(--vio-soft); font-size:.6875rem; padding:.0625rem .5rem; margin-right:.3125rem; }

.plainlist{ list-style:none; }
.plainlist li{
    padding:.25rem 0;
    border-bottom:1px dotted var(--border-dim);
    display:flex; justify-content:space-between; gap:.625rem;
    font-size:.78rem;
}
.plainlist li:last-child{ border-bottom:0; }
.plainlist .note{ color:var(--vio-faint); font-size:.72rem; }

.status{ font-size:.6875rem; border:1px solid var(--border-dim); padding:.0625rem .5rem; color:var(--vio-soft); vertical-align:middle; }
.status--live{ color:#9fd49f; border-color:#2f4a2c; }
.status--wip{ color:var(--gold); border-color:#4a4028; }

/* =======================================================
   BUG OF THE MONTH — carrete
======================================================= */

.bug-stage{
    display:grid;
    grid-template-columns:auto minmax(0,1fr) auto;
    align-items:center;
    gap:.625rem;
}

/* sin JS el deck muestra las seis fichas apiladas, con todo su
   contenido; el JS le pone .is-carousel y pasa a una a la vez */
.bug-deck{ display:grid; gap:.9375rem; min-width:0; }
.bug-deck.is-carousel .bug-slide{ display:none; }
.bug-deck.is-carousel .bug-slide.is-active{ display:grid; }

.bug-arrow{
    background:var(--panel);
    border:1px solid var(--border);
    color:var(--bright);
    font-family:var(--font);
    font-size:1.375rem;
    line-height:1;
    padding:1.25rem .4375rem;
    cursor:pointer;
}
.bug-arrow:hover{ background:var(--titlebar); color:#fff; }

/* miniaturas del archivo: cargan su ficha en el carrete de arriba.
   El botón cubre la tarjeta entera en vez de envolverla, porque un
   <button> no puede contener <div>/<p>. */
.bug-thumb{ position:relative; }
.bug-thumb:hover{ border-color:var(--bright); }
.bug-thumb.is-on{ border-color:var(--gold); }
.bug-thumb__btn{
    position:absolute;
    inset:0;
    border:0;
    background:none;
    padding:0;
    cursor:pointer;
}

.page .mem-photo{ flex:none; aspect-ratio:16/10; }
.page .bug-photo{ flex:none; aspect-ratio:4/3; }
.page .gb-entries{ overflow:visible; }

.col-float{ display:flex; flex-wrap:wrap; gap:2.75rem 3.75rem; justify-content:center; padding:2.75rem .625rem; }
.col-icon{ text-align:center; color:var(--vio); font-size:.8125rem; transition:transform .18s ease; }
.col-icon:hover{ transform:translateY(-.25rem); color:var(--white); }
.col-icon .png{
    width:5.25rem; height:5.25rem;
    margin:0 auto .5rem;
    display:grid; place-items:center;
    font-size:2.5rem;
    filter:drop-shadow(0 0 10px rgba(150,90,240,.35));
}
.col-icon .png img{ width:100%; height:100%; object-fit:contain; }

/* estado de los feeds que llena el JS: "loading…" mientras carga,
   o el motivo si algo falló. Lo usan memories, art, bug y guestbook. */
.feed-status{ color:var(--vio-faint); font-size:.75rem; text-align:center; padding:.5rem 0; }

/* =======================================================
   UNDER CONSTRUCTION
   Lo que todavía no está listo. Un mismo bloque en tres
   tamaños: .uc--slim (la barra de links de la home), .uc a
   secas (widgets de la home) y .uc--page (páginas enteras).
======================================================= */

.uc{
    flex:1;
    min-height:0;
    display:flex;
    flex-direction:column;
    align-items:center;
    justify-content:center;
    text-align:center;
    gap:.375rem;
}

/* cinta de obra: rayas de css, no caracteres, así llena el ancho que sea */
.uc-tape{
    width:100%;
    height:.375rem;
    flex-shrink:0;
    opacity:.45;
    background:repeating-linear-gradient(
        -45deg,
        var(--gold) 0 .1875rem,
        transparent .1875rem .4375rem
    );
}

.uc-sign{ font-size:.66rem; color:var(--vio-soft); }

.uc-title{
    color:var(--gold);
    font-size:.8125rem;
    letter-spacing:.18em;
    text-transform:uppercase;
    animation:uc-blink 1.8s steps(1,end) infinite;
}
@keyframes uc-blink{ 50%{ opacity:.4; } }

.uc-note{ font-size:.75rem; color:var(--vio-soft); max-width:28rem; }
.uc-back{ font-size:.72rem; color:var(--vio-faint); }

/* barra de "links i like": una sola línea, sin cartel */
.uc--slim{ gap:.1875rem; }
.uc--slim .uc-line{ font-size:.72rem; color:var(--vio-soft); }

/* páginas enteras: el mismo bloque, con aire */
.uc--page{ padding:3.25rem 0; gap:.625rem; }
.uc--page .uc-sign{ font-size:.8125rem; margin:.375rem 0; }
.uc--page .uc-title{ font-size:1rem; }

/* =======================================================
   RESPONSIVE — la home fija se vuelve una columna
   Solo en pantallas realmente estrechas: en desktop el
   diseño escala en vez de reordenarse.
======================================================= */

@media (max-width:820px){
    /* misma especificidad que la regla de arriba, pero va después: gana */
    html:has(.layout),
    html:has(.layout) body{ overflow:visible; height:auto; }
    /* el padding de abajo es el hueco del reproductor fijo */
    body{ padding:1.125rem 1.125rem 4.75rem; align-items:flex-start; }

    /* ---------- home: una sola columna, en el orden que se lee ----------
       Los widgets están repartidos en .sidebar, .main y las dos .row, así
       que como hermanos directos no son: order solo ordena entre hermanos.
       display:contents disuelve esas tres cajas intermedias —dejan de
       generar su propio recuadro y sus hijos pasan a ser hijos de .layout—
       y recién ahí se puede pedir cualquier orden sin tocar el HTML.
       Ojo: se cuelga de .layout a propósito, para no disolver también el
       .sidebar de las páginas internas, que es otro. */
    .layout{
        height:auto;
        display:flex;
        flex-direction:column;
    }
    .layout .sidebar,
    .layout .main,
    .layout .row{ display:contents; }

    .layout .hero{ order:1; }
    .layout .nav{ order:2; }
    .layout .welcome{ order:3; }
    .layout .journal{ order:4; }
    .layout .memory{ order:5; }
    .layout .brain{ order:6; }
    .layout .bug{ order:7; }
    .layout .counter{ order:8; }
    .layout .guestbook{ order:9; }
    .layout .links{ order:10; }
    .layout .footer{ order:11; }
    .layout .copyright{ order:12; }

    .welcome,.music,.brain,.bug,.journal,.memory{ grid-column:auto; }
    .bug-photo,.mem-photo{ flex:none; aspect-ratio:4/3; }
    .gb-entries{ overflow:visible; }
    .hero{ min-height:9.375rem; }
    .logo-ascii{ font-size:clamp(4px,1.5vw,9px); }

    /* ---------- bienvenida y obsesión: ascii izquierda, texto derecha ----------
       Los <p> no están envueltos en ningún contenedor, así que no alcanza
       con poner el cuerpo en fila: hay que armar una grilla de dos columnas
       y mandar el <pre> a la primera y los párrafos a la segunda.

       Dos detalles que parecen caprichos y no lo son:

       · el span 99 del dibujo. Con filas implícitas, grid-row:1/-1 no
         abarca nada —el -1 apunta al borde de la grilla explícita, que acá
         no existe— y el ascii quedaría del alto de un renglón. El 99 es
         "todas las que haya", sin tener que contar los párrafos ni volver
         acá si mañana agregás una línea.

       · row-gap en 0. El span crea 99 filas, y un gap entre filas se
         cobra también entre las vacías: con el gap de siempre serían
         98 huecos de aire al fondo de la caja. El aire entre párrafos lo
         ponen sus propios márgenes, como en desktop.

       minmax(0,1fr) y no 1fr para que las mitades sean mitades de verdad:
       con 1fr la columna no baja del ancho del dibujo (es white-space:pre,
       no achica) y se comería más de la mitad. */
    .welcome .win__body,
    .brain .win__body{
        display:grid;
        grid-template-columns:minmax(0,1fr) minmax(0,1fr);
        column-gap:.625rem;
        row-gap:0;
        align-content:center;
        text-align:left;
    }
    .welcome .win__body > .ascii,
    .brain .win__body > .ascii{
        grid-column:1;
        grid-row:1 / span 99;
        align-self:center;
        justify-self:center;
        margin:0;
    }
    .welcome .win__body > p,
    .brain .win__body > p{ grid-column:2; }
    /* el gap:1px que tenía la columna, ahora que row-gap es 0 */
    .welcome .win__body > p{ margin-bottom:1px; }

    /* ---------- el nav, plegado ----------
       El botón toma el lugar del título en la barra: mismo texto, pero
       ahora todo el ancho es zona tocable, no un ícono de 12px. */
    .nav-drop .win__bar .title{ display:none; }
    .nav-toggle{
        display:flex;
        align-items:center;
        justify-content:space-between;
        gap:.5rem;
        flex:1;
        background:none;
        border:0;
        padding:.125rem 0;
        color:var(--bright);
        font-family:var(--font);
        font-size:.75rem;
        text-align:left;
        cursor:pointer;
    }
    .nav-toggle:hover{ color:var(--white); }
    .nav-caret{ font-size:.8125rem; color:var(--vio); line-height:1; }
    .nav-drop.is-open .nav-caret{ transform:rotate(180deg); }

    .nav-drop .nav-list{ display:none; }
    .nav-drop.is-open .nav-list{ display:flex; }

    /* ---------- el reproductor de la home se vuelve la barra de abajo ----------
       En vez de inyectar una .np-bar acá —que duplicaría los ids del
       widget y dejaría dos reproductores peleándose— se reestila el que ya
       existe: mismos controles, mismo JS, solo cambia la forma. Y al ser
       CSS puro se acomoda solo al rotar el teléfono, sin recargar. */
    .music{
        position:fixed;
        left:0; right:0; bottom:0;
        z-index:60;                /* debajo del lightbox (90) */
        display:block;
        border:0;
        border-top:1px solid var(--border);
        background:var(--panel);
    }
    .music .win__bar{ display:none; }   /* el título no entra ni hace falta */
    .music .win__body{
        display:flex;
        flex-direction:row;
        align-items:center;
        gap:.625rem;
        padding:.375rem .75rem;
        overflow:visible;
    }
    .music .np-top{
        display:flex;
        flex-direction:row;
        align-items:center;
        gap:.625rem;
        flex:1;
        min-width:0;
    }
    .music .np-cover{
        width:2.25rem; height:2.25rem;
        flex:none;
        aspect-ratio:auto;
        justify-self:auto;
    }
    .music .np-meta{ flex:1; min-width:0; }
    .music .np-title{ font-size:.8125rem; }
    .music .np-artist{ font-size:.72rem; margin-top:0; }
    .music .np-time{ font-size:.7rem; margin-top:.125rem; }
    .music .np-bottom{
        display:flex;
        align-items:center;
        gap:.75rem;
        flex:none;
    }
    .music .np-bottom .dotrule{ display:none; }
    .music .np-volume{ flex:none; width:5.5rem; }
    .music .np-controls{ gap:1.125rem; padding-top:0; }
}

/* en pantallas muy angostas el tiempo es lo primero que sobra, igual que
   en la .np-bar de las páginas internas */
@media (max-width:600px){
    .music .np-time{ display:none; }
    .music .np-volume{ width:4rem; }
}

@media (prefers-reduced-motion:reduce){
    .gb-track.is-scrolling,.uc-title,.starfield .star,
    .np-line.is-scrolling > span{ animation:none; }
    .welcome-screen,.dither-wrap canvas,.col-icon{ transition:none; }
}

/* fallback si el navegador no soporta :has() */
body.inner{ display:block; padding:0; }
