/* =============================================================================
 * NYU NODE · movimento e estados
 * -----------------------------------------------------------------------------
 * Sete estados, e a regra que os governa é uma só: só `loading` tem laço.
 * Todo o resto acontece uma vez e para. Personagem que se mexe o tempo todo
 * ao lado de um gráfico disputa a atenção com o número, e o número é o produto.
 *
 * TOKENS · este arquivo não cria cor nenhuma. Usa `--nb-teal`, `--nb-amber` e
 * `--nb-down` do sistema, com valor de reserva literal só para o caso de o
 * arquivo ser carregado antes da folha base. Critério 9 da onda: zero token
 * novo, zero valor de token alterado.
 *
 * A CURVA LINEAR DA ÓRBITA É DELIBERADA, e contraria o Art. 3 da Borboleta.
 * O artigo diz "easing nunca linear", e ele está certo para transição: curva
 * é o que faz uma entrada parecer física. Rotação contínua é outra coisa —
 * não tem entrada nem saída, tem só meio. Pôr `ease-in-out` num giro de 360°
 * em laço produz um solavanco visível no ponto de emenda, porque a velocidade
 * chega a zero no fim e recomeça do zero no início do ciclo seguinte. Linear
 * é a única curva sem costura aparente aqui. A exceção está NOMEADA em
 * `tools/movimento.test.js` e é aferida: se esta regra sumir, o teste reprova
 * a exceção órfã.
 * ========================================================================== */

.nyu-node {
  --nyu-node-size: 48px;
  --nyu-node-t: 240ms;
  --nyu-node-curva: cubic-bezier(.22, .8, .24, 1);

  position: relative;
  display: inline-grid;
  place-items: center;
  inline-size: var(--nyu-node-size);
  block-size: var(--nyu-node-size);
  flex: 0 0 auto;
  /* `contain` impede que a animação do mascote force recálculo de layout na
     página inteira — ele vive numa caixa de tamanho fixo e o que muda dentro
     não sai dela. */
  contain: layout paint;
  isolation: isolate;
}

.nyu-node__art,
.nyu-node__fallback {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  display: block;
}

/* O SVG estático aparece PRIMEIRO e sozinho. Só sai quando o inline entrou —
   e se o JavaScript nunca rodar, ele fica, que é o comportamento correto. */
.nyu-node__fallback { object-fit: contain; opacity: 1; transition: opacity var(--nyu-node-t) ease; }
.nyu-node__art      { opacity: 0; transition: opacity var(--nyu-node-t) ease; }
.nyu-node.is-pronto .nyu-node__fallback { opacity: 0; }
.nyu-node.is-pronto .nyu-node__art      { opacity: 1; }

/* ── as peças, dentro do SVG inline ──────────────────────────────────────── */

/* `transform-box: fill-box` faz o transform-origin valer sobre a caixa do
   próprio elemento e não sobre a do SVG. Sem ele, `center` é o centro do
   viewBox e a peça gira em volta do lugar errado. */
.nyu-node__core,
.nyu-node__visor,
.nyu-node__no { transform-box: fill-box; transform-origin: center; }

/* A ÓRBITA gira em volta do centro do CORPO, que fica em (64,54) num viewBox
   de 128 — ou seja 50% 42,19%. Não é o centro da caixa: o nó de baixo e a base
   puxam o desenho para baixo, e girar em volta de 50% 50% faria a constelação
   inteira bambolear. Aqui `fill-box` seria errado de propósito: a origem tem de
   ser a do SVG, não a da caixa do grupo. */
.nyu-node__orbit { transform-origin: 50% 42.19%; }

/* ── loading · o único laço ──────────────────────────────────────────────── */

.nyu-node[data-state="loading"] .nyu-node__orbit {
  animation: nyu-orbita 2400ms linear infinite;
}
.nyu-node[data-state="loading"] .nyu-node__core {
  animation: nyu-respira 1800ms ease-in-out infinite;
}
.nyu-node[data-state="loading"] .nyu-node__visor {
  animation: nyu-pisca 2400ms ease-in-out infinite;
}
.nyu-node[data-state="loading"] .nyu-node__links {
  animation: nyu-sincroniza 2400ms ease-in-out infinite;
}

/* ── updated e success · uma vez, e para ─────────────────────────────────── */

.nyu-node[data-state="updated"] .nyu-node__core,
.nyu-node[data-state="success"] .nyu-node__core {
  animation: nyu-confirma 300ms var(--nyu-node-curva) both;
}
.nyu-node[data-state="updated"] .nyu-node__ring,
.nyu-node[data-state="success"] .nyu-node__ring {
  animation: nyu-anel 320ms var(--nyu-node-curva) both;
}

/* ── warning e error · estáticos, e é de propósito ───────────────────────── */

/* Aviso que pisca é aviso que a pessoa aprende a ignorar, e num produto de
   mercado alavancado o aviso é a informação. Aqui a cor muda e FICA. */
.nyu-node[data-state="warning"] .nyu-node__core   { filter: drop-shadow(0 0 7px var(--nb-amber, #F1A928)); }
.nyu-node[data-state="warning"] .nyu-node__visor  { fill: var(--nb-amber, #F1A928); }
.nyu-node[data-state="error"]   .nyu-node__core   { filter: drop-shadow(0 0 7px var(--nb-down, #F16A6B)); }
.nyu-node[data-state="error"]   .nyu-node__visor  { fill: var(--nb-down, #F16A6B); }
.nyu-node[data-state="connected"] .nyu-node__ring { filter: drop-shadow(0 0 5px var(--nb-teal, #00E5C0)); }

/* `idle` não tem regra nenhuma. É o estado em repouso, e um mascote em repouso
   não se mexe — a ausência de bloco aqui é a decisão, não o esquecimento. */

/* ── os quadros ──────────────────────────────────────────────────────────── */

@keyframes nyu-orbita {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* 3% no máximo, que é o número da especificação do fundador. Acima disso o
   núcleo "bombeia" e o olho vai para ele em vez de ir para o dado. */
@keyframes nyu-respira {
  0%, 100% { transform: scale(1);    opacity: .86; }
  50%      { transform: scale(1.03); opacity: 1; }
}

/* Dois piscares por ciclo, nos tempos da especificação: 0,65s e 1,85s dentro
   de 2,4s — 27% e 77%. O segundo é mais curto que o primeiro, que é como
   piscar de verdade acontece: o par não é simétrico. */
@keyframes nyu-pisca {
  0%, 24%, 31%, 74%, 79%, 100% { opacity: 1; }
  27%, 77%                     { opacity: .12; }
}

/* As ligações acendem em sequência sugerindo sincronização. Amplitude baixa:
   entre .45 e .95, nunca apagando de vez — linha que some vira falha, e o que
   se quer comunicar é fluxo, não queda. */
@keyframes nyu-sincroniza {
  0%, 100% { opacity: .5; }
  30%      { opacity: .95; }
  62%      { opacity: .55; }
}

@keyframes nyu-confirma {
  0%   { transform: scale(.97); opacity: .74; }
  62%  { transform: scale(1.03); opacity: 1; }
  100% { transform: scale(1);   opacity: 1; }
}

@keyframes nyu-anel {
  0%   { transform: scale(.92); opacity: .55; }
  55%  { transform: scale(1.08); opacity: 1; }
  100% { transform: scale(1);   opacity: 1; }
}

/* ── tamanhos ────────────────────────────────────────────────────────────── */

.nyu-node--pequeno { --nyu-node-size: 30px; }
.nyu-node--grande  { --nyu-node-size: 96px; }

/* ── balão de ajuda · o ponto de contato ─────────────────────────────────── */

/* `min-block-size: 44px` no gatilho porque ele é alvo de toque, e 44 é o
   número que este projeto adotou. Não é enfeite: o portão `toque.spec.js`
   mede isto no pixel com ponteiro grosso. */
.nyu-help { position: relative; min-width: 0; margin-top: var(--sp-3, 12px); }

.nyu-help__trigger {
  display: inline-flex; align-items: center; gap: 8px;
  min-block-size: 44px; max-inline-size: 100%;
  padding: 4px 12px 4px 4px;
  font: inherit; font-size: 13px; color: var(--nb-ink-2, #b8c6da);
  background: transparent;
  border: 1px solid var(--nb-line, rgba(255,255,255,.1));
  border-radius: var(--r-md, 10px);
  cursor: pointer; text-align: left;
}
.nyu-help__trigger:hover,
.nyu-help__trigger:focus-visible {
  color: var(--nb-ink, #E7EEF8);
  border-color: var(--nb-line-2, rgba(0,229,192,.3));
}

.nyu-help__painel {
  max-inline-size: min(380px, 100%);
  margin-block-start: 8px; padding: 12px 14px;
  font-size: 13px; line-height: 1.55;
  color: var(--nb-ink-2, #b8c6da);
  background: var(--nb-panel, #101E33);
  border: 1px solid var(--nb-line-2, rgba(0,229,192,.24));
  border-radius: var(--r-md, 10px);
}
.nyu-help__painel p { margin: 0 0 8px; }
.nyu-help__painel a { color: var(--nb-teal, #00E5C0); }
/* `[hidden]` sozinho perde para `display:flex`/`grid` herdado de um pai —
   por isso o `!important`, que aqui não é preguiça, é a única forma de o
   atributo continuar significando o que diz. */
.nyu-help__painel[hidden] { display: none !important; }

@media (max-width: 620px) {
  .nyu-node          { --nyu-node-size: 40px; }
  .nyu-node--pequeno { --nyu-node-size: 28px; }
  .nyu-node--grande  { --nyu-node-size: 72px; }
}

/* ── quem pede menos movimento ───────────────────────────────────────────── */

/* Congela no quadro neutro, não some. O mascote continua ali dizendo o que
   diz; o que ele perde é o movimento. E `animation-iteration-count:1` está
   aqui porque duração de .01ms sozinha não para um `infinite` — ela reinicia,
   e reiniciar milhares de vezes por segundo é pior que a animação original. */
@media (prefers-reduced-motion: reduce) {
  .nyu-node *,
  .nyu-node::before,
  .nyu-node::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}
