/* ===== v2026 · slide-in panel + content typography (cards/skills/timeline/contacts/node) ===== */

/* timeline accent tokens (used by .p-era and friends) */
:root {
    --tl-neon:     #6fd6ff;
    --tl-phosphor: #b9ff66;
}

/* ===== Slide-in panel ===== */
.panel {
    position: fixed;
    top: 76px;
    right: var(--pad);
    bottom: 50px;
    width: min(950px, 66vw);
    z-index: 35;
    background: var(--hud-bg-2);
    border: 1px solid var(--border-2);
    border-radius: 10px;
    box-shadow:
        0 0 0 1px rgba(111, 214, 255, 0.10),
        0 26px 60px rgba(3, 8, 30, 0.65),
        0 0 56px rgba(42, 92, 255, 0.16);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    display: flex; flex-direction: column;
    opacity: 0;
    transform: translateX(40px);
    pointer-events: none;
    transition: opacity 0.42s cubic-bezier(0.2, 0.8, 0.2, 1), transform 0.42s cubic-bezier(0.2, 0.8, 0.2, 1);
    overflow: hidden;
}
.panel[data-open="true"] {
    /* transform:none (not translateX(0)) — a live transform on the panel makes it a
       containing block/composited layer and, combined with backdrop-filter + overflow
       hidden, iOS Safari stops hit-testing touch scroll on .panel-body. */
    opacity: 1; transform: none; pointer-events: auto;
}

/* Fullscreen variant (labs) — covers the viewport like the old alice overlay. */
.panel.panel-full {
    top: 0; right: 0; bottom: 0; left: 0;
    width: auto;
    border-radius: 0;
    z-index: 60;
}
/* Estação solta (fora do grid) em painel de tela cheia. Dentro do `.lab-grid` a
   altura é a da linha do grid — o seletor abaixo é mais específico que
   `.lab-grid .lab-crt` e venceria, deixando as capas de /labs com 64vh cada. */
.panel.panel-full :not(.lab-grid) > .lab-station .lab-crt { height: min(900px, 64vh); }

/* ===== Labs — retro arcade stations (matches the pixel office vibe) =====
   Cada estação é só a CAPA do lab: o clique abre o site no overlay de tela
   cheia (mesma janela do desktop linux). Nada é embutido aqui dentro — em meia
   coluna do painel um site inteiro fica espremido e ilegível. */
/* Duas colunas, sempre. Com três, a quarta capa ficava órfã ocupando um terço
   da linha e dois terços vazios ao lado — e cada capa tinha ~470px, largura em
   que o título já entrava em reticência. Em duas, /labs fecha 2×2 simétrico,
   Projetos (2 capas) fecha numa linha só, e a largura dobra: nenhum título é
   cortado. `auto-rows: 1fr` iguala as duas linhas — sem isso a de baixo
   encolhe para o conteúdo dela e o bloco desanda. */
.lab-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    grid-auto-rows: 1fr;
    gap: 20px;
    align-items: stretch;
    margin-top: 20px;
}
/* /labs abre em painel de tela cheia e a grade toma o corpo inteiro: sem título
   e sem linha de apoio (saíram em 08/08) sobram ~150px de altura, e as quatro
   capas crescem para dentro do espaço em vez de deixar faixa morta em volta.
   `width: 100%` é obrigatório junto do `margin-inline: auto` — o corpo do painel
   é um flex column, e margem auto num item flex desliga o stretch: sem ele a
   grade encolhe para o conteúdo e sobram 200px mortos de cada lado. O teto de
   1180px que existia aqui foi embora com o mesmo raciocínio. */
.panel-full .lab-grid { width: 100%; margin-inline: auto; }
/* Painel lateral (Projetos) tem ~660px de corpo, contra os 1438px de /labs: em
   duas colunas cada capa fica com 263px, largura em que o título é cortado no
   meio (`CASE·01 — AUDIOFLOW …`) e o terminal perde o fim de toda linha. Em
   coluna única a capa vai a 545px e o texto inteiro cabe — as duas estações
   empilhadas somam 408px num corpo de 660, sem rolagem. */
.panel:not(.panel-full) .lab-grid { grid-template-columns: 1fr; grid-auto-rows: auto; }
/* Uma coluna só quando a segunda deixaria de caber: abaixo disso a capa fica
   estreita demais para a arte e para o título na mesma linha. */
@media (max-width: 900px) {
    .lab-grid { grid-template-columns: 1fr; grid-auto-rows: auto; }
}
.lab-grid .lab-station { margin-top: 0; display: flex; flex-direction: column; }
.lab-grid .lab-crt  { flex: 1 1 auto; height: auto; min-height: min(320px, 36vh); }

.lab-station {
    margin-top: 20px;
    border: 2px solid #1d2f55;
    border-radius: 4px;
    background: #070b18;
    box-shadow: 0 0 0 1px rgba(111, 214, 255, 0.07), 0 10px 30px rgba(3, 8, 30, 0.5);
    overflow: hidden;
}
.lab-bar {
    display: flex; align-items: center; gap: 10px;
    padding: 10px 12px;
    font-family: "JetBrains Mono", monospace;
    font-size: 10.5px; letter-spacing: 2px; text-transform: uppercase;
    color: #9fd8ff;
    border-bottom: 2px solid #1d2f55;
    background: linear-gradient(180deg, #0d1730, #0a1124);
}
.lab-bar-t { font-weight: 700; }
/* Título e host dividem a barra e ela não pode quebrar em duas linhas — a
   estação ficaria mais baixa que a vizinha e o topo desalinha. Em duas colunas
   sobra largura e nenhum título chega a reticência; ela fica como rede, e é o
   HOST que encolhe primeiro (`min-width: 0` no `em`), porque o nome do lab
   informa mais que o subdomínio, repetido no botão logo abaixo. */
.lab-grid .lab-bar { letter-spacing: 1.4px; }
.lab-grid .lab-bar-t { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lab-bar em { margin-left: auto; font-style: normal; color: #4d6da6; letter-spacing: 1px; text-transform: none; }
.lab-grid .lab-bar em {
    flex: 0 1 auto; min-width: 0;
    font-size: 9.5px; letter-spacing: 0.5px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.lab-led {
    flex: 0 0 auto;
    width: 8px; height: 8px;            /* square = pixel LED */
    background: #41ff8b;
    box-shadow: 0 0 8px #41ff8b;
    /* Aceso fixo, não piscando. Os quatro LEDs pulsavam em períodos diferentes
       (1,4s e 2,1s) no alto de cada capa: quatro pontos brilhantes competindo
       pela atenção com o conteúdo do cartão, que é o que se quer ler. O
       `@keyframes lab-led-blink` FICA — outros quatro elementos ainda o usam
       (barra do status, boot log e os dois painéis de projeto). */
}
.lab-led-y { background: #ffe066; box-shadow: 0 0 8px #ffe066; }
.lab-led-g { background: #41ff8b; box-shadow: 0 0 8px #41ff8b; }
@keyframes lab-led-blink { 50% { opacity: 0.25; box-shadow: none; } }

/* --- CRT: a capa da estação. O clique não liga nada aqui — abre o lab no
       overlay de tela cheia (openLabStation em js/panels.js). --- */
.lab-crt {
    position: relative;
    height: min(480px, 50vh);
    background: #02040c;
    overflow: hidden;
    cursor: pointer;
}
.lab-crt:hover { background: #05091a; }
.lab-crt:focus-visible { outline: 2px solid #ffe066; outline-offset: -2px; }
.lab-crt::after {                       /* scanlines over everything */
    content: '';
    position: absolute; inset: 0;
    background: repeating-linear-gradient(0deg, rgba(160, 200, 255, 0.05) 0 1px, transparent 1px 3px);
    pointer-events: none;
    z-index: 3;
}
/* Duas faixas, não uma pilha centrada. A arte de cada lab tem altura própria
   (`.lab-voice` ~112px, `.lab-embed-art` ~49px, `.lab-term` 3 linhas);
   centralizando o conjunto inteiro, a linha de baixo caía numa altura diferente
   em cada capa e as quatro ficavam desencontradas lado a lado. Com o conteúdo na faixa elástica
   e o rodapé numa faixa de tamanho próprio, ele nasce sempre na mesma linha — é
   ela que o olho segue ao varrer o grid.
   Eram três faixas até 08/08: a do meio era o `PRESS START`, que saiu — o
   próprio cartão inteiro já é clicável e a chamada só roubava altura de quem
   diz alguma coisa. */
.lab-crt-off {
    position: absolute; inset: 0;
    display: grid;
    grid-template-rows: minmax(0, 1fr) auto;
    align-items: center; justify-items: center;
    gap: 16px;
    padding: 18px 18px 20px;
    z-index: 2;
    background: radial-gradient(ellipse at center, #0a1228 0%, #02040c 75%);
}
/* O conteúdo cabe na faixa elástica ou é cortado — nunca empurra o rodapé para
   fora da capa. Daí o `minmax(0, 1fr)` acima: com `1fr` puro a faixa não encolhe
   abaixo do min-content do terminal, e a quarta linha do LAB·02 transbordava. */
.lab-crt-off > :first-child { align-self: center; max-height: 100%; overflow: hidden; }
/* `.lab-voice` e `.lab-embed-art` já nascem no tamanho da capa larga — foram
   desenhados aqui, não herdados da capa estreita de três colunas. O `scale(3.2)`
   que engordava a onda e o vetor antigos saiu junto com eles. */
/* full-bleed: a estação escapa o padding lateral/superior do .panel-body (22px) */
.lab-station.lab-bleed {
    margin: -22px -22px 30px;
    border-width: 0 0 2px;
    border-radius: 0;
    box-shadow: 0 14px 34px rgba(3, 8, 30, 0.55);
}

/* Saíram daqui em 08/08/2026, quando /projects virou estação como /labs:
   `.lab-embed`, `.proj-direct`, `.proj-reader`, `.proj-back`, `.proj-embed`,
   `.p-card.proj-card` e `.p-card-cta`. Todas serviam ao leitor embutido — o
   artigo abria DENTRO do painel, num iframe de altura fixa com botão "voltar" e
   sem mexer na URL. Agora o artigo sobe no mesmo overlay de tela cheia dos labs
   e a rota é `/projects/<slug>`, então nenhuma delas tem mais elemento no DOM
   (`grep -c` no index.html: 0 para cada). `.p-card` sozinha FICA — outros
   painéis a usam. */
/* === LAB·01 — a voz da Alice =========================================
   Substituiu em 08/08/2026 as nove barrinhas de `.lab-wave`, que subiam e
   desciam em `steps(3)` e podiam ilustrar qualquer coisa com som. Aqui o
   desenho conta o que o lab faz: alguém fala, e a onda espelhada responde —
   como qualquer visualizador de áudio de verdade, crescendo do eixo para os
   dois lados.
   Houve um segundo bloco embaixo, com quatro falas alternando quem tinha a
   palavra; saiu no mesmo dia, a pedido. Roteirinho de conversa na capa era
   texto para ler numa arte que já se explica sozinha — e a onda ganhou a
   altura dele. */
.lab-voice { display: flex; flex-direction: column; align-items: center; }
.lab-voice-w {
    display: flex; align-items: center; gap: 4px;
    /* 112px, não 74: a onda é a arte inteira da capa desde que as falas saíram. */
    height: 112px;
}
.lab-voice-w span {
    width: 5px; height: 8px;
    border-radius: 3px;
    background: linear-gradient(180deg, #6fd6ff, #41ff8b);
    /* `scaleY` a partir do centro dá a simetria de graça — animar `height` com
       `align-items: center` faria o mesmo, mas repaginando o layout 60× por
       segundo em 21 elementos. Transform não toca no layout. */
    animation: lab-voice-w 1.15s ease-in-out infinite alternate;
}
/* Envelope de fala: alto no meio, baixo nas pontas — é o formato que uma frase
   tem num visualizador. Sem isso as 21 barras pulsam iguais e vira equalizador
   de brinquedo. Os delays quebram a sincronia e dão o movimento de onda. */
.lab-voice-w span:nth-child(11n+1) { animation-delay: 0.00s; --peak: 1.4; }
.lab-voice-w span:nth-child(2n)    { animation-delay: 0.09s; --peak: 3.1; }
.lab-voice-w span:nth-child(3n)    { animation-delay: 0.19s; --peak: 5.2; }
.lab-voice-w span:nth-child(4n)    { animation-delay: 0.05s; --peak: 4.0; }
.lab-voice-w span:nth-child(5n)    { animation-delay: 0.27s; --peak: 6.8; }
.lab-voice-w span:nth-child(7n)    { animation-delay: 0.14s; --peak: 8.2; }
.lab-voice-w span:nth-child(9n)    { animation-delay: 0.22s; --peak: 2.6; }
@keyframes lab-voice-w {
    from { transform: scaleY(0.5); opacity: 0.45; }
    to   { transform: scaleY(var(--peak, 3)); opacity: 1; }
}

/* === LAB·02 — o índice sendo lido =====================================
   O que este lab é: um arquivo que o agente lê inteiro, direto, sem varrer o
   site. Então a capa mostra o arquivo — as linhas acendem de cima para baixo,
   no ritmo de quem lê — e, ao fim da leitura, os três agentes acendem: leram.
   Era um `.lab-term` de três linhas de texto até 08/08/2026; as linhas diziam
   a mesma coisa que o `.lab-sub` já diz, e não se mexiam. */
/* A faixa elástica da capa mede 128px (medido por `shot-panel.py`), e o
   `.lab-crt-off` a corta com `overflow: hidden` — sem erro, sem aviso. Foi o que
   aconteceu na primeira versão desta arte: com `line-height` herdado, o bloco
   pedia 138px e os três agentes nasciam fora da moldura. Daí o `line-height`
   explícito aqui e no `.lab-llms-bots`: a altura desta arte é uma conta, não um
   acaso do que a folha de cima definiu. */
.lab-llms-art { display: grid; justify-items: center; gap: 10px; }
.lab-llms-file {
    display: grid; gap: 3px;
    font-family: "JetBrains Mono", monospace;
    font-size: 13px; line-height: 1.5; letter-spacing: 0.4px;
    /* Alinhado à esquerda por dentro, centrado por fora: é um arquivo, e arquivo
       tem margem esquerda reta. O `.lab-llms-art` centra o bloco na capa. */
    text-align: left;
}
.lab-llms-file span {
    color: #8fb4e8;
    opacity: 0.22;
    animation: lab-llms-line 5.4s ease-in-out infinite;
}
/* A cascata é a leitura: 0,26s entre linhas, rápido o bastante para parecer
   varredura e lento o bastante para o olho seguir. */
.lab-llms-file span:nth-child(1) { animation-delay: 0.00s; color: #e8f1ff; }
.lab-llms-file span:nth-child(2) { animation-delay: 0.26s; }
.lab-llms-file span:nth-child(3) { animation-delay: 0.52s; }
.lab-llms-file span:nth-child(4) { animation-delay: 0.78s; }
@keyframes lab-llms-line {
    0%, 100%  { opacity: 0.22; transform: translateX(0); }
    9%, 62%   { opacity: 1;    transform: translateX(4px); }
    75%       { opacity: 0.22; transform: translateX(0); }
}
/* Os três agentes só acendem DEPOIS da última linha (1,6s adiante): a ordem é
   o argumento do lab — o arquivo é lido primeiro, o consumo vem de graça. */
.lab-llms-bots { display: flex; gap: 8px; }
.lab-llms-bots b {
    font-family: "JetBrains Mono", monospace;
    font-size: 11px; line-height: 1.2; font-weight: 400; letter-spacing: 1px;
    padding: 3px 9px;
    border: 1px solid #1d2f55;
    border-radius: 3px;
    color: #41ff8b;
    opacity: 0.25;
    animation: lab-llms-bot 5.4s ease-in-out infinite;
}
.lab-llms-bots b:nth-child(1) { animation-delay: 1.60s; }
.lab-llms-bots b:nth-child(2) { animation-delay: 1.85s; }
.lab-llms-bots b:nth-child(3) { animation-delay: 2.10s; }
@keyframes lab-llms-bot {
    0%, 100% { opacity: 0.25; border-color: #1d2f55; }
    8%, 48%  { opacity: 1;    border-color: #41ff8b; }
    62%      { opacity: 0.25; border-color: #1d2f55; }
}

/* === LAB·04 — o ranking que muda =====================================
   A frase do lab é "quem ganha depende do TIPO de pergunta". Uma tabela parada
   não diz isso; seis barras que crescem e encolhem em onda, com o verde da
   liderança passando de uma para outra, dizem — é a mesma medição, lida de
   novo a cada pergunta. Substituiu o `.lab-term` estático em 08/08/2026.
   Um keyframe só, seis delays: as seis percorrem o mesmo ciclo defasadas, o
   que basta para o topo trocar de dono. Seis keyframes separados dariam o
   mesmo efeito com seis vezes mais CSS para manter em dia. */
/* Seis linhas contra os 128px da faixa: o `line-height` explícito é o que faz a
   conta fechar. Com o herdado, a sexta barra (`RRF+revisor`) nascia cortada pelo
   `overflow: hidden` do `.lab-crt-off` — silenciosamente, porque a régua da capa
   mede o cartão, não a arte. */
.lab-rank { display: grid; gap: 4px; width: 100%; max-width: 420px; }
.lab-rank-row { display: grid; grid-template-columns: 96px minmax(0, 1fr); align-items: center; gap: 10px; }
.lab-rank-row i {
    font-family: "JetBrains Mono", monospace;
    font-size: 11.5px; line-height: 1.3; font-style: normal; letter-spacing: 0.6px;
    color: #8fb4e8;
    text-align: right;
    white-space: nowrap;
}
.lab-rank-row b {
    height: 9px;
    border-radius: 2px;
    background: #2a4a86;
    /* `scaleX` a partir da esquerda: a barra cresce da origem, como barra de
       medida faz. Animar `width` daria o mesmo desenho relayout-ando a linha
       inteira 60× por segundo. */
    transform-origin: left center;
    transform: scaleX(0.3);
    animation: lab-rank-b 4.8s ease-in-out infinite;
}
.lab-rank-row:nth-child(1) b { animation-delay: 0.0s; }
.lab-rank-row:nth-child(2) b { animation-delay: 0.8s; }
.lab-rank-row:nth-child(3) b { animation-delay: 1.6s; }
.lab-rank-row:nth-child(4) b { animation-delay: 2.4s; }
.lab-rank-row:nth-child(5) b { animation-delay: 3.2s; }
.lab-rank-row:nth-child(6) b { animation-delay: 4.0s; }
/* Verde só no pico: em qualquer quadro, uma ou duas barras estão na frente e o
   resto está atrás — que é exatamente o que a comparação mede. */
@keyframes lab-rank-b {
    0%, 100% { transform: scaleX(0.30); background: #2a4a86; }
    35%      { transform: scaleX(1.00); background: #41ff8b; }
    70%      { transform: scaleX(0.52); background: #2a4a86; }
}

/* === LAB·03 — a frase virando vetor ==================================
   Substituiu os doze quadradinhos de `.lab-vec`, que piscavam em opacidade sem
   dizer de onde vinham. Embedding é uma coisa só: um pedaço de conteúdo entra e
   sai como N números. Então é isso que a capa mostra, em loop — a frase se
   apaga, as coordenadas acendem no lugar dela, e volta. */
.lab-embed-art { display: grid; justify-items: center; gap: 16px; }
/* Frase e vetor dividem a célula, como as falas do LAB·01: trocam sem deslocar
   o que está em volta. */
/* `justify-items: center` é obrigatório: a célula fica com a largura do MAIOR
   dos dois (o vetor), e sem ele a frase — mais curta — encostava na borda
   esquerda dessa célula e nascia visivelmente fora do eixo da capa. */
.lab-embed-swap { display: grid; height: 26px; align-items: center; justify-items: center; }
.lab-embed-swap > * { grid-area: 1 / 1; }
.lab-embed-txt, .lab-embed-num {
    font-family: "JetBrains Mono", monospace;
    white-space: nowrap;
    animation: lab-embed-swap 6s ease-in-out infinite;
}
.lab-embed-txt { font-size: 15px; color: #e8f1ff; }
/* O vetor entra 3s depois — meio ciclo, o tempo que a frase fica na tela. */
.lab-embed-num { font-size: 13.5px; color: #41ff8b; animation-delay: 3s; letter-spacing: 0.4px; }
@keyframes lab-embed-swap {
    0%, 50%, 100% { opacity: 0; transform: translateY(6px); }
    6%, 44%       { opacity: 1; transform: translateY(0); }
}
/* Os pontos abaixo são o espaço vetorial: acendem da esquerda para a direita no
   compasso da troca, como se as dimensões fossem preenchidas uma a uma. */
.lab-embed-dots { display: flex; gap: 6px; }
.lab-embed-dots span {
    width: 7px; height: 7px;
    border-radius: 50%;
    background: #6fd6ff;
    opacity: 0.16;
    animation: lab-embed-dot 6s ease-in-out infinite;
}
.lab-embed-dots span:nth-child(1)  { animation-delay: 3.00s; }
.lab-embed-dots span:nth-child(2)  { animation-delay: 3.12s; }
.lab-embed-dots span:nth-child(3)  { animation-delay: 3.24s; background: #41ff8b; }
.lab-embed-dots span:nth-child(4)  { animation-delay: 3.36s; }
.lab-embed-dots span:nth-child(5)  { animation-delay: 3.48s; }
.lab-embed-dots span:nth-child(6)  { animation-delay: 3.60s; background: #41ff8b; }
.lab-embed-dots span:nth-child(7)  { animation-delay: 3.72s; }
.lab-embed-dots span:nth-child(8)  { animation-delay: 3.84s; background: #ffe066; }
.lab-embed-dots span:nth-child(9)  { animation-delay: 3.96s; }
.lab-embed-dots span:nth-child(10) { animation-delay: 4.08s; background: #41ff8b; }
.lab-embed-dots span:nth-child(11) { animation-delay: 4.20s; }
.lab-embed-dots span:nth-child(12) { animation-delay: 4.32s; }
@keyframes lab-embed-dot {
    0%, 100% { opacity: 0.16; transform: scale(1); }
    8%, 46%  { opacity: 1;    transform: scale(1.35); }
    60%      { opacity: 0.16; transform: scale(1); }
}
/* === CASE·01 — o áudio virando texto ==================================
   A capa é o produto inteiro numa figura: chega uma mensagem de voz, ela toca,
   e a transcrição nasce embaixo palavra por palavra. A cabeça de reprodução é
   a onda acendendo da esquerda para a direita — as barras não pulam ao acaso,
   elas marcam onde o áudio está.
   Como nas capas de /labs, `line-height` é explícito: a faixa elástica do
   `.lab-crt-off` mede 128px e corta com `overflow: hidden`, sem erro nenhum.
   Aqui a conta fecha em ~86px, com folga de sobra.
   Escala revista em 08/08 depois de olhar o print lado a lado com /labs: na
   primeira medida a arte tinha 67px contra os 112–118px das capas de labs, e a
   bolha ficava perdida no meio da capa. As proporções internas não mudaram —
   tudo cresceu junto, ~1,3×. */
.proj-audio { display: grid; justify-items: center; gap: 14px; }
.proj-audio-bubble {
    display: flex; align-items: center; gap: 12px;
    padding: 10px 16px;
    background: #10233f;
    border: 1px solid #23406e;
    /* canto vivo embaixo à esquerda: é a bolha de quem RECEBE a mensagem */
    border-radius: 16px 16px 16px 5px;
}
.proj-audio-play {
    width: 0; height: 0;
    border-left: 13px solid #41ff8b;
    border-top: 8px solid transparent;
    border-bottom: 8px solid transparent;
}
.proj-audio-wave { display: flex; align-items: center; gap: 4px; height: 26px; }
.proj-audio-wave i {
    width: 4px;
    background: #2a4a86;
    border-radius: 2px;
    animation: proj-audio-bar 3.6s linear infinite;
}
/* Alturas irregulares porque forma de onda é irregular — nove barras do mesmo
   tamanho viram um equalizador de enfeite, não um áudio. */
.proj-audio-wave i:nth-child(1) { height: 9px;  animation-delay: 0.00s; }
.proj-audio-wave i:nth-child(2) { height: 18px; animation-delay: 0.09s; }
.proj-audio-wave i:nth-child(3) { height: 26px; animation-delay: 0.18s; }
.proj-audio-wave i:nth-child(4) { height: 14px; animation-delay: 0.27s; }
.proj-audio-wave i:nth-child(5) { height: 22px; animation-delay: 0.36s; }
.proj-audio-wave i:nth-child(6) { height: 12px; animation-delay: 0.45s; }
.proj-audio-wave i:nth-child(7) { height: 20px; animation-delay: 0.54s; }
.proj-audio-wave i:nth-child(8) { height: 16px; animation-delay: 0.63s; }
.proj-audio-wave i:nth-child(9) { height: 8px;  animation-delay: 0.72s; }
@keyframes proj-audio-bar {
    0%        { background: #2a4a86; transform: scaleY(0.5); }
    6%        { background: #41ff8b; transform: scaleY(1); }
    70%       { background: #41ff8b; transform: scaleY(0.9); }
    78%, 100% { background: #2a4a86; transform: scaleY(0.5); }
}
.proj-audio-time {
    font-family: "JetBrains Mono", monospace;
    font-size: 12px; line-height: 1.2; letter-spacing: 0.6px;
    color: #6f90c4;
}
.proj-audio-text {
    display: flex; gap: 6px;
    font-family: "JetBrains Mono", monospace;
    font-size: 14px; line-height: 1.4; letter-spacing: 0.4px;
    color: #e8f1ff;
    white-space: nowrap;
}
/* As 7 palavras casam com os 7 `nth-child` de delay abaixo: mudou a frase,
   mude os delays junto — palavra sem delay entra com 0 e aparece antes das
   anteriores, que é a mesma armadilha do boot log (§15 do CLAUDE.md). */
.proj-audio-text span {
    opacity: 0;
    animation: proj-audio-word 3.6s ease-out infinite;
}
.proj-audio-text span:nth-child(1) { animation-delay: 0.75s; }
.proj-audio-text span:nth-child(2) { animation-delay: 0.91s; }
.proj-audio-text span:nth-child(3) { animation-delay: 1.07s; }
.proj-audio-text span:nth-child(4) { animation-delay: 1.23s; }
.proj-audio-text span:nth-child(5) { animation-delay: 1.39s; }
.proj-audio-text span:nth-child(6) { animation-delay: 1.55s; }
.proj-audio-text span:nth-child(7) { animation-delay: 1.71s; }
@keyframes proj-audio-word {
    0%        { opacity: 0; transform: translateY(2px); }
    7%, 62%   { opacity: 1; transform: none; }
    72%, 100% { opacity: 0; transform: translateY(2px); }
}

/* === CASE·02 — o robô que se corrige sozinho ==========================
   O artigo defende o anti-queda em domínio puro; a capa mostra exatamente
   isso. O robô tomba devagar e volta RÁPIDO, passando do ponto antes de
   assentar — é correção ativa, não pêndulo, e a diferença de velocidade entre
   ir e voltar é o que faz a figura dizer "ele não caiu porque reagiu".
   Os olhos amarelam no pico da inclinação e voltam a verde quando o eixo
   fecha: o mesmo semáforo do LED da barra do cartão.
   Altura da arte: 30 + 4 + 38 + 4 + 28 + 8 + 2 = 114px, dentro dos 128px — e na
   mesma faixa das capas de /labs (112–118px), que é o que faz as duas grades
   parecerem a mesma coisa. Na primeira medida o robô tinha 92px e ficava miúdo
   ao lado delas. */
.proj-k1 { display: grid; justify-items: center; gap: 8px; }
.proj-k1-bot {
    display: grid; justify-items: center; gap: 4px;
    /* pivô nos pés: girar pelo centro faria o robô flutuar em vez de tombar */
    transform-origin: bottom center;
    animation: proj-k1-tilt 4.2s ease-in-out infinite;
}
.proj-k1-head {
    display: flex; align-items: center; justify-content: center; gap: 9px;
    width: 42px; height: 30px;
    background: #16294a;
    border: 1px solid #2c4c82; border-radius: 7px;
}
.proj-k1-eye {
    width: 6px; height: 6px; border-radius: 50%;
    background: #41ff8b; box-shadow: 0 0 8px rgba(65, 255, 139, 0.7);
    animation: proj-k1-eye 4.2s ease-in-out infinite;
}
.proj-k1-torso {
    width: 34px; height: 38px;
    background: linear-gradient(#1b3357, #12233f);
    border: 1px solid #2c4c82; border-radius: 6px;
}
.proj-k1-legs { display: flex; gap: 10px; }
.proj-k1-legs i {
    width: 7px; height: 28px;
    background: #1b3357;
    border: 1px solid #2c4c82; border-radius: 0 0 3px 3px;
}
.proj-k1-floor { width: 176px; height: 2px; background: #2a4a86; }
@keyframes proj-k1-tilt {
    0%        { transform: rotate(0deg); }
    26%       { transform: rotate(9deg); }      /* tomba: lento */
    34%       { transform: rotate(-3.5deg); }   /* corrige: rápido, passa do ponto */
    40%       { transform: rotate(1.2deg); }
    46%, 58%  { transform: rotate(0deg); }
    76%       { transform: rotate(-8deg); }     /* agora para o outro lado */
    82%       { transform: rotate(3deg); }
    88%       { transform: rotate(-1deg); }
    94%, 100% { transform: rotate(0deg); }
}
@keyframes proj-k1-eye {
    0%, 20%   { background: #41ff8b; box-shadow: 0 0 8px rgba(65, 255, 139, 0.7); }
    26%, 32%  { background: #ffe066; box-shadow: 0 0 9px rgba(255, 224, 102, 0.8); }
    40%, 70%  { background: #41ff8b; box-shadow: 0 0 8px rgba(65, 255, 139, 0.7); }
    76%, 82%  { background: #ffe066; box-shadow: 0 0 9px rgba(255, 224, 102, 0.8); }
    90%, 100% { background: #41ff8b; box-shadow: 0 0 8px rgba(65, 255, 139, 0.7); }
}

/* O `.lab-press` (“▸ PRESS START · …”) morreu em 08/08/2026: piscava em amarelo
   no meio de cada capa, roubava a faixa central do grid e não dizia nada que a
   barra do topo e o botão do rodapé já não dissessem. O `@keyframes lab-blink`
   FICA — `.lab-cursor` e o boot log ainda o usam. */
.lab-sub {
    font-family: "JetBrains Mono", monospace;
    /* Última linha da capa e único texto de algumas delas: sobe de 10.5px para
       13px e clareia, porque agora é ela que carrega os números do lab. */
    font-size: 13px; letter-spacing: 1.8px;
    color: #8fb4e8;
    /* Uma linha por capa. Quebrando em duas, ela sobe só naquele cartão e o
       alinhamento do rodapé entre os quatro se perde. */
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    max-width: 100%;
    text-align: center;
}
.lab-crt:hover .lab-sub { color: #b9d4ff; }

/* --- terminal (llms.txt): lines type themselves in --- */
.lab-term {
    padding: 14px 16px 12px;
    font-family: "JetBrains Mono", monospace;
    font-size: 12px; line-height: 1.8;
    color: var(--tl-phosphor);
    background: #030710;
}
/* Dentro da capa da estação o terminal é ilustração, não bloco de conteúdo:
   sem fundo próprio, largura contida e o que não couber é cortado (as linhas
   são `nowrap` por causa da animação de digitação). */
.lab-crt-off .lab-term {
    width: 100%;
    /* Teto de medida: sem ele o texto encosta nas duas bordas da capa larga e
       fica à esquerda enquanto a arte dos outros labs está centrada. 640px é a
       linha mais longa em uso (55 caracteres a 15px ≈ 495px) com folga. */
    max-width: 640px;
    padding: 0;
    background: transparent;
    /* Sem o `PRESS START` a faixa do meio some e sobra altura: o terminal é o
       conteúdo do cartão, então cresce de 11.5px para 15px. */
    font-size: 15px; line-height: 2.1;
    overflow: hidden;
}
.lab-term .ln {
    display: block;
    max-width: 0;
    overflow: hidden; white-space: nowrap; text-overflow: clip;
    animation: lab-type 0.9s steps(36, end) forwards;
}
.lab-term .ln-cmd { color: #e8f1ff; }
.lab-term .ln:nth-child(1) { animation-delay: 0.15s; }
.lab-term .ln:nth-child(2) { animation-delay: 1.05s; }
.lab-term .ln:nth-child(3) { animation-delay: 1.95s; }
.lab-term .ln:nth-child(4) { animation-delay: 2.85s; }
@keyframes lab-type { from { max-width: 0; } to { max-width: 100%; } }
.lab-cursor {
    display: inline-block;
    width: 8px; height: 13px;
    margin-left: 3px; vertical-align: -2px;
    background: var(--tl-phosphor);
    animation: lab-blink 0.8s steps(2, end) infinite;
}
.lab-btn {
    display: block;
    padding: 11px 16px;
    font-family: "JetBrains Mono", monospace;
    font-size: 11.5px; font-weight: 700; letter-spacing: 2px;
    color: #ffe066; text-decoration: none; text-align: center;
    border-top: 2px solid #1d2f55;
    background: #0a1124;
    transition: background 0.15s, color 0.15s;
}
.lab-btn:hover { background: #142251; color: #fff3b0; }
/* mesma barra usada como <button> (projetos: abre o artigo embutido em vez de
   navegar) — zera o chrome nativo e mantém o visual do <a class="lab-btn">. */
button.lab-btn {
    width: 100%;
    border: 0; border-top: 2px solid #1d2f55;
    cursor: pointer;
    font-family: "JetBrains Mono", monospace;
}

@media (prefers-reduced-motion: reduce) {
    /* As quatro capas contam sua história pelo movimento: sem ele, o que sobra é
       o QUADRO FINAL de cada uma — a onda parada, o arquivo lido, o ranking de
       uma pergunta só, o vetor no lugar da frase. Por isso cada animação parada
       ganha aqui a opacidade que teria no fim do ciclo; sem essas linhas, os
       elementos que nascem em `opacity: 0` ficariam invisíveis para sempre. */
    .lab-cursor, .lab-voice-w span,
    .lab-llms-file span, .lab-llms-bots b,
    .lab-rank-row b,
    .lab-embed-txt, .lab-embed-num, .lab-embed-dots span { animation: none; }
    .lab-voice-w span { transform: scaleY(2.2); opacity: 0.7; }
    /* Parado, o LAB·02 mostra o fim da leitura: arquivo inteiro aceso, três
       agentes acesos — que é o estado que o lab defende. */
    .lab-llms-file span { opacity: 1; transform: none; }
    .lab-llms-bots b { opacity: 1; border-color: #41ff8b; }
    /* Parado, o ranking vira a medição de uma pergunta só: barras de tamanhos
       diferentes, com a primeira na frente. Todas iguais não compararia nada. */
    .lab-rank-row b { background: #2a4a86; }
    .lab-rank-row:nth-child(1) b { transform: scaleX(1.00); background: #41ff8b; }
    .lab-rank-row:nth-child(2) b { transform: scaleX(0.86); }
    .lab-rank-row:nth-child(3) b { transform: scaleX(0.71); }
    .lab-rank-row:nth-child(4) b { transform: scaleX(0.60); }
    .lab-rank-row:nth-child(5) b { transform: scaleX(0.48); }
    .lab-rank-row:nth-child(6) b { transform: scaleX(0.35); }
    /* Parado, o LAB·03 mostra o lado de saída: o vetor, que é o que o lab produz. */
    .lab-embed-txt { opacity: 0; }
    .lab-embed-num { opacity: 1; transform: none; }
    .lab-embed-dots span { opacity: 0.65; transform: none; }
    .lab-term .ln { animation: none; max-width: 100%; }
    /* As duas capas de /projects seguem a mesma ideia: parado, o que sobra é o
       quadro final. O áudio já tocou (onda inteira verde, frase transcrita
       completa) e o robô está de pé com os olhos verdes — que é a tese do
       artigo, não um robô a caminho do chão. */
    .proj-audio-wave i, .proj-audio-text span,
    .proj-k1-bot, .proj-k1-eye { animation: none; }
    .proj-audio-wave i { background: #41ff8b; }
    .proj-audio-text span { opacity: 1; transform: none; }
}
.panel-head {
    flex: 0 0 auto;
    display: flex; align-items: center; gap: 14px;
    padding: 14px 18px;
    border-bottom: 1px solid var(--border);
    font-family: var(--font-mono);
    font-size: 11px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--fg-dim);
    background: rgba(7, 11, 24, 0.4);
}
.panel-no  {
    color: var(--accent);
    font-weight: 600;
    font-size: 13px;
}
.panel-tag { color: var(--fg); flex: 1; }
.panel-close {
    appearance: none;
    background: transparent;
    border: 1px solid var(--border);
    color: var(--fg);
    width: 28px; height: 28px;
    border-radius: 6px;
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
.panel-close:hover {
    background: rgba(248, 113, 113, 0.12);
    border-color: rgba(248, 113, 113, 0.5);
    color: #fca5a5;
}
.panel-body {
    flex: 1 1 auto;
    /* Default flex-item min-height is `auto` (= content size) which makes the
       body grow to fit content and silently disables overflow scroll. */
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;   /* momentum scroll on iOS */
    touch-action: pan-y;                 /* declare the gesture explicitly for WebKit */
    padding: 22px 22px 26px;
}
.panel-body::-webkit-scrollbar { width: 8px; }
.panel-body::-webkit-scrollbar-thumb { background: rgba(111, 214, 255, 0.25); border-radius: 4px; }
.panel-body::-webkit-scrollbar-track { background: transparent; }

/* Panel decorative corners */
.panel-corners span {
    position: absolute;
    width: 14px; height: 14px;
    border-color: var(--accent);
    border-style: solid;
    pointer-events: none;
    opacity: 0.7;
}
.panel-corners span:nth-child(1) { top: 6px;    left: 6px;    border-width: 1px 0 0 1px; }
.panel-corners span:nth-child(2) { top: 6px;    right: 6px;   border-width: 1px 1px 0 0; }
.panel-corners span:nth-child(3) { bottom: 6px; left: 6px;    border-width: 0 0 1px 1px; }
.panel-corners span:nth-child(4) { bottom: 6px; right: 6px;   border-width: 0 1px 1px 0; }

/* ===== Panel content typography ===== */
.p-title {
    font-size: clamp(22px, 2.4vw, 32px);
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.15;
    margin-bottom: 14px;
    color: var(--fg);
}
.p-lead {
    font-size: 14.5px;
    color: var(--fg);
    line-height: 1.65;
    margin-bottom: 18px;
}
.p-bullets { list-style: none; display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px; }
.p-bullets li {
    position: relative;
    padding-left: 22px;
    font-size: 14px;
    color: var(--fg);
}
.p-bullets li::before {
    content: "";
    position: absolute; left: 0; top: 11px;
    width: 10px; height: 1px;
    background: var(--accent);
}

/* Panel · cards (open source) — arcade cartridges */
.p-cards {
    display: flex; flex-direction: column; gap: 14px;
    margin-top: 14px;
}
.p-card {
    display: block;
    padding: 0;
    background: #070b18;
    border: 2px solid #1d2f55;
    border-radius: 4px;
    overflow: hidden;
    position: relative;
    box-shadow: 0 0 0 1px rgba(111, 214, 255, 0.07), 0 10px 30px rgba(3, 8, 30, 0.5);
    transition: border-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}
.p-card::after {                        /* scanlines on hover — CRT feel */
    content: '';
    position: absolute; inset: 0;
    background: repeating-linear-gradient(0deg, rgba(160, 200, 255, 0.05) 0 1px, transparent 1px 3px);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s ease;
}
.p-card:hover {
    border-color: rgba(111, 214, 255, 0.55);
    transform: translateY(-2px);
    box-shadow: 0 0 0 1px rgba(111, 214, 255, 0.15), 0 14px 36px rgba(3, 8, 30, 0.6), 0 0 24px rgba(111, 214, 255, 0.12);
}
.p-card:hover::after { opacity: 1; }
.p-card-h {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: 10px 14px;
    border-bottom: 2px solid #1d2f55;
    background: linear-gradient(180deg, #0d1730, #0a1124);
}
.p-card-h span {
    display: inline-flex; align-items: center; gap: 10px;
    font-family: "JetBrains Mono", monospace;
    font-size: 13.5px; font-weight: 700; letter-spacing: 1px;
    color: #9fd8ff;
}
.p-card-h span::before {                /* pixel LED, color cycles per card */
    content: '';
    width: 8px; height: 8px; flex: 0 0 auto;
    background: #41ff8b; box-shadow: 0 0 8px #41ff8b;
    animation: lab-led-blink 1.4s steps(2, end) infinite;
}
.p-cards .p-card:nth-child(2) .p-card-h span::before { background: #6fd6ff; box-shadow: 0 0 8px #6fd6ff; animation-duration: 1.8s; }
.p-cards .p-card:nth-child(3) .p-card-h span::before { background: #ffe066; box-shadow: 0 0 8px #ffe066; animation-duration: 2.1s; }
.p-card-h em {
    font-family: var(--font-mono);
    font-style: normal;
    font-size: 10.5px;
    color: #4d6da6;
    letter-spacing: 1px;
}
.p-card p {
    font-size: 13.5px;
    color: var(--fg-dim);
    line-height: 1.6;
    margin: 0;
    padding: 12px 14px 10px;
}
.p-tags { display: flex; flex-wrap: wrap; gap: 5px; padding: 0 14px 14px; }
.p-tags span {
    font-family: var(--font-mono);
    font-size: 10.5px;
    padding: 3px 8px;
    border: 1px solid rgba(111, 214, 255, 0.32);
    border-radius: 3px;
    color: rgba(200, 232, 255, 0.92);
    background: rgba(7, 11, 24, 0.55);
    letter-spacing: 0.08em;
}

/* Panel · skills — cartridge slots */
.p-skillgrid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
    margin-bottom: 16px;
}
.p-skill {
    padding: 12px 14px;
    border: 2px solid #1d2f55;
    border-radius: 4px;
    background: #070b18;
    position: relative;
    overflow: hidden;
    transition: border-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}
.p-skill::before {                      /* power strip on the left edge */
    content: '';
    position: absolute; left: 0; top: 0; bottom: 0;
    width: 3px;
    background: linear-gradient(180deg, #41ff8b, rgba(65, 255, 139, 0.08));
    opacity: 0.55;
    transition: opacity 0.2s ease;
}
.p-skillgrid .p-skill:nth-child(2n)::before  { background: linear-gradient(180deg, #6fd6ff, rgba(111, 214, 255, 0.08)); }
.p-skillgrid .p-skill:nth-child(3n)::before  { background: linear-gradient(180deg, #ffe066, rgba(255, 224, 102, 0.08)); }
.p-skill:hover {
    border-color: rgba(111, 214, 255, 0.55);
    transform: translateY(-2px);
    box-shadow: 0 10px 26px rgba(3, 8, 30, 0.55), 0 0 18px rgba(111, 214, 255, 0.10);
}
.p-skill:hover::before { opacity: 1; }
.p-skill h3 {
    font-family: var(--font-mono);
    font-size: 10.5px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: #9fd8ff;
    margin-bottom: 6px;
}
.p-skill p {
    font-size: 13px;
    color: var(--fg-dim);
    line-height: 1.55;
}
.p-skill h3 em {
    font-style: normal;
    float: right;
    color: var(--fg-mute);
    font-family: var(--font-mono);
    font-size: 9.5px;
    letter-spacing: 0.18em;
    font-weight: 400;
}
.p-pills {
    display: flex; flex-wrap: wrap; gap: 6px;
    margin-top: 10px;
}
.p-pills span {
    font-family: var(--font-mono);
    font-size: 11px;
    padding: 4px 10px;
    border: 1px solid rgba(111, 214, 255, 0.32);
    border-radius: 3px;
    color: rgba(200, 232, 255, 0.92);
    background: rgba(7, 11, 24, 0.55);
    letter-spacing: 0.06em;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.p-pills span:hover {
    border-color: #ffe066;
    color: #fff3b0;
    background: #142251;
}

/* Panel · timeline (compact list) */
.p-timeline {
    list-style: none;
    display: flex; flex-direction: column;
    border-left: 1px solid var(--border);
    padding-left: 22px;
    margin: 6px 0 18px;
}
.p-timeline li {
    position: relative;
    padding: 10px 0;
    display: grid;
    grid-template-columns: 120px 1fr;
    gap: 18px;
}
.p-timeline li::before {
    content: "";
    position: absolute;
    left: -27px; top: 18px;
    width: 8px; height: 8px;            /* square = pixel marker */
    background: var(--accent);
    box-shadow: 0 0 0 4px var(--bg), 0 0 10px var(--accent);
    animation: lab-led-blink 2.2s steps(2, end) infinite;
}
.p-timeline li:nth-child(2n)::before { background: var(--tl-phosphor); box-shadow: 0 0 0 4px var(--bg), 0 0 10px var(--tl-phosphor); animation-delay: 0.7s; }
.p-timeline strong {
    font-family: var(--font-mono);
    font-size: 12px;
    color: var(--accent);
    letter-spacing: 0.06em;
}
.p-timeline span {
    font-size: 13.5px;
    color: var(--fg);
}

/* Panel · contacts — arcade high-score table (.lab-station chrome) */
.ct-station { margin-top: 6px; }
.ct-list { list-style: none; margin: 0; }
.ct-row {
    display: grid;
    grid-template-columns: 52px 110px 1fr auto;
    align-items: center; gap: 14px;
    padding: 14px 16px;
    font-family: "JetBrains Mono", monospace;
    border-bottom: 2px solid #101b38;
    text-decoration: none;
    transition: background 0.15s;
}
.ct-row:hover { background: #142251; }
.ct-rank {
    font-size: 12px; font-weight: 700; letter-spacing: 1px;
    color: #4d6da6;
}
.ct-list li:nth-child(1) .ct-rank { color: #ffe066; text-shadow: 0 0 10px rgba(255, 224, 102, 0.45); }
.ct-list li:nth-child(2) .ct-rank { color: #cfe2ff; }
.ct-list li:nth-child(3) .ct-rank { color: #ffb077; }
.ct-chan {
    font-size: 10.5px; font-weight: 700; letter-spacing: 2px; text-transform: uppercase;
    color: #9fd8ff;
}
.ct-handle {
    font-size: 13px; color: var(--fg);
    overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
    transition: color 0.15s;
}
.ct-row:hover .ct-handle { color: #fff3b0; }
.ct-go {
    font-size: 13px; color: #4d6da6;
    transition: color 0.15s, transform 0.15s;
}
.ct-row:hover .ct-go { color: #ffe066; transform: translate(2px, -2px); }
.ct-term { border-top: 2px solid #1d2f55; }
@media (max-width: 640px) {
    .ct-row { grid-template-columns: 40px 1fr auto; gap: 10px; }
    .ct-chan { display: none; }
}

/* Panel · about — boot log (terminal lines that type themselves in) */
.ab-station { margin: 4px 0 18px; }
.ab-log {
    padding: 14px 16px 12px;
    font-family: "JetBrains Mono", monospace;
    font-size: 12.5px; line-height: 2;
    background: #030710;
}
.ab-log .bl {
    display: flex; align-items: baseline; gap: 8px;
    color: var(--fg);
    opacity: 0;
    animation: p-rise 0.4s cubic-bezier(0.2, 0.8, 0.2, 1) forwards;
}
.ab-log .bl b { color: #6fd6ff; font-weight: 700; flex: 0 0 auto; }
.ab-log .bl i {
    margin-left: auto; flex: 0 0 auto;
    font-style: normal; font-size: 10px; font-weight: 700; letter-spacing: 2px;
    color: var(--tl-phosphor);
    text-shadow: 0 0 8px rgba(185, 255, 102, 0.4);
}
.ab-log .bl:last-child i { color: #ffe066; text-shadow: 0 0 8px rgba(255, 224, 102, 0.5); animation: lab-blink 1.2s steps(2, end) infinite; }
.ab-log .bl:nth-child(1) { animation-delay: 0.10s; }
.ab-log .bl:nth-child(2) { animation-delay: 0.28s; }
.ab-log .bl:nth-child(3) { animation-delay: 0.46s; }
.ab-log .bl:nth-child(4) { animation-delay: 0.64s; }
.ab-log .bl:nth-child(5) { animation-delay: 0.82s; }
.ab-log .bl:nth-child(6) { animation-delay: 1.00s; }
.p-foot {
    margin-top: 14px;
    padding-top: 14px;
    border-top: 1px solid var(--border);
    font-family: var(--font-mono);
    font-size: 10.5px;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--fg-mute);
}

/* Panel · node detail */
.p-node-meta {
    display: flex; align-items: center; gap: 10px;
    margin-bottom: 10px;
    font-family: var(--font-mono);
    font-size: 10.5px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
}
.p-node-layer {
    padding: 3px 8px;
    border: 1px solid currentColor;
    border-radius: 4px;
}
.p-node-layer[data-layer="edge"]    { color: var(--layer-edge); }
.p-node-layer[data-layer="net"]     { color: var(--layer-net); }
.p-node-layer[data-layer="orch"]    { color: var(--layer-orch); }
.p-node-layer[data-layer="compute"] { color: var(--layer-compute); }
.p-node-layer[data-layer="obs"]     { color: var(--layer-obs); }
.p-node-stack { color: var(--fg-dim); }
.p-node-title { font-size: clamp(24px, 2.4vw, 36px); }

/* Panel · node detail chips (since / era / autoral) */
.p-chips {
    display: flex; flex-wrap: wrap; gap: 6px;
    margin: 6px 0 14px;
}
.p-chip {
    font-family: var(--font-mono);
    font-size: 10.5px;
    padding: 3px 9px;
    border: 1px solid var(--border-2);
    border-radius: 3px;
    color: var(--fg-dim);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    background: rgba(7, 11, 24, 0.55);
}
.p-chip-star {
    color: #0a0e0a;
    background: linear-gradient(135deg, #ffd66a, #ffb000);
    border-color: rgba(255, 176, 0, 0.7);
    font-weight: 700;
    letter-spacing: 0.08em;
}
.p-chip[data-era="edge"]  { color: var(--layer-edge);    border-color: rgba(122, 220, 255, 0.4); }
.p-chip[data-era="cloud"] { color: var(--layer-net);     border-color: rgba(155, 139, 255, 0.4); }
.p-chip[data-era="kube"]  { color: var(--layer-orch);    border-color: rgba(56, 189, 248, 0.4); }
.p-chip[data-era="ai"]    { color: var(--layer-compute); border-color: rgba(251, 146, 60, 0.45); }

/* Panel · timeline (cinematic 7-era cards from career slide) */
.p-tl-head {
    display: flex; align-items: center; gap: 10px;
    flex-wrap: wrap;
    margin: 4px 0 14px;
    font-family: var(--font-mono);
    font-size: 10px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--fg-mute);
}
.p-tl-eyebrow {
    padding: 3px 8px;
    border: 1px solid var(--border-2);
    border-radius: 3px;
    color: var(--tl-neon);
    border-color: rgba(111, 214, 255, 0.4);
}
.p-tl-live {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 3px 8px;
    border: 1px solid rgba(185, 255, 102, 0.45);
    border-radius: 3px;
    color: var(--tl-phosphor);
}
.p-tl-dot {
    width: 6px; height: 6px;
    border-radius: 50%;
    background: var(--tl-phosphor);
    box-shadow: 0 0 8px var(--tl-phosphor);
    animation: tl-pulse 1.6s ease-in-out infinite;
}
@keyframes tl-pulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50%      { opacity: 0.4; transform: scale(0.7); }
}
.p-tl-years { color: var(--fg-dim); }

.p-eras {
    list-style: none;
    display: flex; flex-direction: column;
    gap: 14px;
    margin: 18px 0 18px;
    position: relative;
}
.p-eras::before {
    content: "";
    position: absolute;
    left: 11px; top: 12px; bottom: 12px;
    width: 1px;
    background: linear-gradient(
        180deg,
        rgba(111, 214, 255, 0.0) 0%,
        rgba(111, 214, 255, 0.35) 12%,
        rgba(185, 255, 102, 0.35) 50%,
        rgba(111, 214, 255, 0.35) 88%,
        rgba(111, 214, 255, 0.0) 100%
    );
    pointer-events: none;
}

.p-era {
    position: relative;
    padding: 14px 16px 14px 38px;
    border: 2px solid #1d2f55;
    border-radius: 4px;
    overflow: hidden;
    background: linear-gradient(
        180deg,
        rgba(11, 17, 32, 0.72) 0%,
        rgba(7, 11, 24, 0.85) 100%
    );
    transition: border-color 0.3s ease, transform 0.3s ease, box-shadow 0.3s ease;
}
.p-era::after {                          /* scanlines on hover */
    content: '';
    position: absolute; inset: 0;
    background: repeating-linear-gradient(0deg, rgba(160, 200, 255, 0.05) 0 1px, transparent 1px 3px);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s ease;
}
.p-era:hover {
    transform: translateX(2px);
    border-color: rgba(111, 214, 255, 0.5);
    box-shadow: 0 8px 24px rgba(8, 22, 44, 0.55), 0 0 18px rgba(111, 214, 255, 0.10);
}
.p-era:hover::after { opacity: 1; }
.p-era::before {
    content: "";
    position: absolute;
    left: 6px; top: 18px;
    width: 10px; height: 10px;          /* square pixel marker */
    background: var(--tl-neon);
    box-shadow:
        0 0 0 3px rgba(7, 11, 24, 1),
        0 0 12px var(--tl-neon);
    animation: lab-led-blink 2.4s steps(2, end) infinite;
}
.p-era[data-accent="phosphor"]::before {
    background: var(--tl-phosphor);
    box-shadow:
        0 0 0 3px rgba(7, 11, 24, 1),
        0 0 12px var(--tl-phosphor);
}
.p-era[data-accent="phosphor"] .p-era-year { color: var(--tl-phosphor); }
.p-era[data-accent="phosphor"] .p-era-stack span {
    border-color: rgba(185, 255, 102, 0.35);
    color: rgba(220, 250, 200, 0.92);
}

.p-era-head {
    display: flex; align-items: baseline; flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 6px;
}
.p-era-year {
    font-family: var(--font-mono);
    font-size: 18px;
    font-weight: 700;
    color: var(--tl-neon);
    letter-spacing: 0.04em;
}
.p-era-eyebrow {
    font-family: var(--font-mono);
    font-size: 11px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--fg-mute);
}
.p-era-body {
    font-size: 13.5px;
    line-height: 1.55;
    color: var(--fg);
    margin: 4px 0 10px;
}
.p-era-body strong {
    color: var(--fg);
    font-weight: 600;
}
.p-era-stack {
    display: flex; flex-wrap: wrap; gap: 5px;
}
.p-era-stack span {
    font-family: var(--font-mono);
    font-size: 10.5px;
    padding: 3px 8px;
    border: 1px solid rgba(111, 214, 255, 0.32);
    border-radius: 3px;
    color: rgba(200, 232, 255, 0.92);
    letter-spacing: 0.08em;
    text-transform: lowercase;
    background: rgba(7, 11, 24, 0.55);
}

.p-era-now {
    border-color: rgba(185, 255, 102, 0.45);
    background: linear-gradient(
        180deg,
        rgba(20, 36, 18, 0.72) 0%,
        rgba(8, 14, 24, 0.92) 100%
    );
}

.p-tl-foot {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px;
    margin-top: 18px;
    padding-top: 14px;
    border-top: 1px solid var(--border);
    font-family: var(--font-mono);
    font-size: 10.5px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--fg-mute);
}
.p-tl-foot span:first-child { color: var(--tl-phosphor); }

/* Era cards collapse a touch on small screens — lives with the component */
@media (max-width: 640px) {
    .p-era { padding: 12px 12px 12px 32px; }
    .p-era::before { left: 4px; top: 16px; width: 10px; height: 10px; }
    .p-eras::before { left: 9px; }
    .p-era-year { font-size: 16px; }
    .p-era-body { font-size: 12.5px; }
    .p-era-stack span { font-size: 9.5px; padding: 2px 6px; }
}

.p-cta-row {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 16px;
}
.p-cta {
    display: inline-flex;
    align-items: center; gap: 8px;
    padding: 11px 18px;
    background: linear-gradient(135deg, var(--accent), var(--accent-deep));
    border: 1px solid rgba(111, 214, 255, 0.5);
    border-radius: 999px;
    color: #ffffff;
    font-family: var(--font-mono);
    font-size: 12px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    transition: transform 0.2s ease, box-shadow 0.2s ease, background 0.2s ease, border-color 0.2s ease;
    box-shadow: 0 8px 24px rgba(42, 92, 255, 0.35);
}
.p-cta:hover {
    transform: translateY(-2px);
    box-shadow: 0 12px 32px rgba(42, 92, 255, 0.55);
}

/* Secondary CTA — outline style for "ver detalhes" docs link.
   Visually subordinate to the primary "abrir projeto" gradient pill. */
.p-cta-secondary {
    background: transparent;
    color: var(--accent);
    border-color: rgba(111, 214, 255, 0.35);
    box-shadow: none;
}
.p-cta-secondary:hover {
    background: rgba(111, 214, 255, 0.08);
    border-color: var(--accent);
    box-shadow: 0 6px 20px rgba(111, 214, 255, 0.22);
}

/* ===== Arcade reveal — panel content rises in, staggered. Replays on every
   open because openPanel() re-sets panelBody.innerHTML. ===== */
@keyframes p-rise {
    from { opacity: 0; transform: translateY(12px); }
    to   { opacity: 1; transform: none; }
}
.panel-body .p-title { animation: p-rise 0.4s cubic-bezier(0.2, 0.8, 0.2, 1) backwards; }
.panel-body .p-lead  { animation: p-rise 0.4s cubic-bezier(0.2, 0.8, 0.2, 1) 0.08s backwards; }
:is(.p-cards, .p-skillgrid, .p-eras, .p-timeline, .ct-list, .lab-grid) > * {
    animation: p-rise 0.5s cubic-bezier(0.2, 0.8, 0.2, 1) backwards;
}
:is(.p-cards, .p-skillgrid, .p-eras, .p-timeline, .ct-list, .lab-grid) > :nth-child(1) { animation-delay: 0.06s; }
:is(.p-cards, .p-skillgrid, .p-eras, .p-timeline, .ct-list, .lab-grid) > :nth-child(2) { animation-delay: 0.14s; }
:is(.p-cards, .p-skillgrid, .p-eras, .p-timeline, .ct-list, .lab-grid) > :nth-child(3) { animation-delay: 0.22s; }
:is(.p-cards, .p-skillgrid, .p-eras, .p-timeline, .ct-list, .lab-grid) > :nth-child(4) { animation-delay: 0.30s; }
:is(.p-cards, .p-skillgrid, .p-eras, .p-timeline, .ct-list, .lab-grid) > :nth-child(5) { animation-delay: 0.38s; }
:is(.p-cards, .p-skillgrid, .p-eras, .p-timeline, .ct-list, .lab-grid) > :nth-child(6) { animation-delay: 0.46s; }
:is(.p-cards, .p-skillgrid, .p-eras, .p-timeline, .ct-list, .lab-grid) > :nth-child(7) { animation-delay: 0.54s; }
:is(.p-cards, .p-skillgrid, .p-eras, .p-timeline, .ct-list, .lab-grid) > :nth-child(8) { animation-delay: 0.62s; }
:is(.p-cards, .p-skillgrid, .p-eras, .p-timeline, .ct-list, .lab-grid) > :nth-child(9) { animation-delay: 0.70s; }

@media (prefers-reduced-motion: reduce) {
    .panel-body .p-title,
    .panel-body .p-lead,
    :is(.p-cards, .p-skillgrid, .p-eras, .p-timeline, .ct-list, .lab-grid) > *,
    .p-card-h span::before, .p-skill::before, .p-era::before,
    .p-timeline li::before, .p-tl-dot { animation: none; }
    .ab-log .bl { animation: none; opacity: 1; }
}

/* ===== OSI · layer explainer + node panels (arcade stations, --lc = layer
   color set inline by osi.js). Band click → .ly-station; chip → .nd-station. */
.ly-station .lab-bar-t,
.nd-station .lab-bar-t { color: var(--lc); text-shadow: 0 0 10px color-mix(in srgb, var(--lc) 45%, transparent); }
.ly-led {
    background: var(--lc);
    box-shadow: 0 0 8px var(--lc);
}
.panel-body > .lab-station { animation: p-rise 0.45s cubic-bezier(0.2, 0.8, 0.2, 1) backwards; }

/* --- two-column body: mini stack | info --- */
.ly-body {
    display: grid;
    grid-template-columns: 195px 1fr;
}
.ly-stack {
    display: flex; flex-direction: column; gap: 4px;
    padding: 12px 10px;
    border-right: 2px solid #101b38;
    background: #050912;
}
.ly-row {
    display: flex; align-items: center; gap: 7px;
    padding: 4px 8px;
    border: 2px solid transparent;
    border-radius: 3px;
    font-family: "JetBrains Mono", monospace;
    font-size: 11px; letter-spacing: 0.5px;
    color: #44598a;
    white-space: nowrap;
    animation: p-rise 0.35s cubic-bezier(0.2, 0.8, 0.2, 1) backwards;
}
.ly-row:nth-child(1) { animation-delay: 0.05s; }
.ly-row:nth-child(2) { animation-delay: 0.10s; }
.ly-row:nth-child(3) { animation-delay: 0.15s; }
.ly-row:nth-child(4) { animation-delay: 0.20s; }
.ly-row:nth-child(5) { animation-delay: 0.25s; }
.ly-row:nth-child(6) { animation-delay: 0.30s; }
.ly-row:nth-child(7) { animation-delay: 0.35s; }
.ly-row b { color: var(--lc); font-weight: 700; min-width: 9px; }
.ly-row.is-here {
    flex-wrap: wrap;                     /* tag drops to its own line — never clips */
    border-color: var(--lc);
    color: #e8f1ff;
    background: color-mix(in srgb, var(--lc) 12%, transparent);
    box-shadow: 0 0 14px color-mix(in srgb, var(--lc) 30%, transparent);
}
.ly-row.is-here i {
    flex: 1 0 100%;
    box-sizing: border-box;
    margin-top: 2px;
    padding-left: 16px;
    font-style: normal; font-size: 8.5px; font-weight: 700; letter-spacing: 1px;
    color: var(--lc);
    animation: lab-blink 1.1s steps(2, end) infinite;
}

.ly-info { padding: 15px 16px 14px; }
.ly-desc {
    font-size: 14.5px; line-height: 1.65;
    color: #b9c8e8;
}

/* --- mini topology strip: lit boxes = devices that read this layer --- */
.ly-topo {
    display: flex; align-items: center;
    margin: 14px 0 12px;
}
.ly-tn {
    flex: 0 0 auto;
    padding: 5px 7px;
    border: 2px solid #16223f;
    border-radius: 3px;
    font-family: "JetBrains Mono", monospace;
    font-size: 9.5px; letter-spacing: 1px; text-transform: uppercase;
    color: #3c5180;
    white-space: nowrap;
}
.ly-tn.on {
    border-color: var(--lc);
    color: var(--lc);
    box-shadow: 0 0 10px color-mix(in srgb, var(--lc) 35%, transparent);
    animation: ly-pulse 2.2s ease-in-out infinite;
}
.ly-tc { flex: 1 1 auto; height: 2px; background: #16223f; }
.ly-tc.on { background: color-mix(in srgb, var(--lc) 60%, transparent); }
@keyframes ly-pulse { 50% { opacity: 0.55; } }

.ly-scope {
    font-family: "JetBrains Mono", monospace;
    font-size: 12px; line-height: 1.7;
    color: #7e93bd;
}
.ly-scope b {
    display: block;
    font-size: 10px; letter-spacing: 2px;
    color: var(--lc);
    margin-bottom: 4px;
}
.ly-protos {
    display: flex; flex-wrap: wrap; gap: 6px;
    margin-top: 13px;
}
.ly-protos span,
.nd-tags span {
    padding: 4px 9px;
    border: 1px solid rgba(111, 214, 255, 0.32);
    border-radius: 3px;
    font-family: "JetBrains Mono", monospace;
    font-size: 11px; letter-spacing: 0.5px;
    color: #9fd8ff;
}
.ly-term, .nd-term { border-top: 2px solid #1d2f55; font-size: 13px; }

/* --- nodes running at this layer (chips → node panel) --- */
.ly-nodes { margin-top: 16px; animation: p-rise 0.45s cubic-bezier(0.2, 0.8, 0.2, 1) 0.15s backwards; }
.ly-nodes-t {
    font-family: "JetBrains Mono", monospace;
    font-size: 11px; letter-spacing: 2px;
    color: #4d6da6;
    margin-bottom: 10px;
}
.ly-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.ly-chip {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 8px 12px;
    border: 2px solid #1d2f55;
    border-radius: 3px;
    background: #070b18;
    font-family: "JetBrains Mono", monospace;
    font-size: 12px; letter-spacing: 0.5px;
    color: #9fd8ff;
    cursor: pointer;
    transition: background 0.15s, color 0.15s, border-color 0.15s, transform 0.15s;
    animation: p-rise 0.4s cubic-bezier(0.2, 0.8, 0.2, 1) backwards;
}
.ly-chip:nth-child(1) { animation-delay: 0.20s; }
.ly-chip:nth-child(2) { animation-delay: 0.28s; }
.ly-chip:nth-child(3) { animation-delay: 0.36s; }
.ly-chip:nth-child(4) { animation-delay: 0.44s; }
.ly-chip .dot {
    width: 6px; height: 6px;            /* square pixel LED */
    background: #41ff8b;
    box-shadow: 0 0 6px #41ff8b;
    animation: lab-led-blink 1.6s steps(2, end) infinite;
}
.ly-chip:hover {
    background: #142251;
    border-color: rgba(111, 214, 255, 0.6);
    color: #fff3b0;
    transform: translateY(-1px);
}
.ly-chip.is-primary { border-color: rgba(255, 224, 102, 0.45); color: #ffe066; }
.ly-chip.is-primary .dot { background: #ffe066; box-shadow: 0 0 6px #ffe066; }
.ly-chip.is-primary:hover { border-color: #ffe066; color: #fff3b0; }

/* --- node (project) station --- */
.nd-body { padding: 16px; }
.nd-flag {
    display: inline-block;
    margin-bottom: 10px;
    font-family: "JetBrains Mono", monospace;
    font-size: 10.5px; font-weight: 700; letter-spacing: 2px;
    color: #ffe066;
    text-shadow: 0 0 10px rgba(255, 224, 102, 0.45);
}
.nd-blurb {
    font-size: 15px; line-height: 1.7;
    color: #c3d2f0;
}
.nd-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 13px; }
button.nd-layerbtn {
    width: 100%;
    border: 0; border-top: 2px solid #1d2f55;
    color: var(--lc);
    cursor: pointer;
}

@media (max-width: 640px) {
    .ly-body { grid-template-columns: 1fr; }
    .ly-stack {
        flex-direction: row; flex-wrap: wrap;
        border-right: 0;
        border-bottom: 2px solid #101b38;
    }
    .ly-row .ly-row-n { display: none; }
    .ly-row.is-here .ly-row-n { display: inline; }
}

@media (prefers-reduced-motion: reduce) {
    .panel-body > .lab-station, .ly-nodes, .ly-row, .ly-chip { animation: none; }
    .ly-tn.on, .ly-chip .dot, .ly-row.is-here i { animation: none; }
}

/* ===== Tráfego — Packet Highway (fullscreen, server rack do office) ===== */
.hw-station {
    margin-top: 12px;
    display: flex;
    flex-direction: column;
    height: calc(100vh - 150px);
}
.hw-frame {
    flex: 1 1 auto;
    width: 100%;
    border: 0;
    display: block;
    background: #05060a;
}
