/* efeitos.css · camada de efeitos do Tourmaline (módulo à parte).
   Carrega depois de fonts.css, tokens.css e components.css, e junto de efeitos.js.
   Reúne dois conjuntos de efeitos, os dois no claro e no escuro:
   - ASCII e interação: campo de glifos, varredura, fio, feixe, holofote, inclinação, botão magnético,
     rastro de cursor, nome que falha, terminal que digita, gráfico e retrato em ASCII;
   - dither Bayer em três tons: faixa, dissolve, esfera, sombra pontilhada do botão, régua, rampa, ícones em pixel.
   Regras que não se deduzem:
   - o dither tem três tons e todos vêm de token: --cor-dither-0 (canvas do tema), --cor-dither-1 (o teal de
     área, teal-area) e --cor-dither-2 (cartão do tema). Nunca o teal elétrico do CTA, que pontilhado ficaria neon.
   - nenhuma cor literal aqui: só token, ou color-mix() de token. As máscaras de SVG usam "black" porque só
     o canal alfa conta.
   - efeito de CSS puro liga por classe (.fx-regua, .fx-feixe, .fx-sombra, .fx-ic); efeito com JavaScript liga
     por data-fx="nome" (vários, separados por espaço). Nada se aplica a seletor genérico.
   - o que se lê (terminal, valor, rastro) fica em 14px ou mais; campo, retrato, avatar e barras de glifos são
     desenho, ficam aria-hidden e não carregam informação.
   - canvas e PNG de dither levam image-rendering:pixelated; efeito de mouse só com (hover:hover) and (pointer:fine).
   - com movimento reduzido tudo para: o campo, a faixa e a esfera ficam num quadro só. */

/* trecho que força um tema (data-theme="dark" ou "light"): pinta o fundo do tema, que o body só pinta na página */
.fx-tema{ background:var(--color-canvas); color:var(--color-corpo); }

/* ============ campo de glifos ============ */
/* <pre class="fx-campo" data-fx="campo" aria-hidden="true"> dentro de um bloco com position:relative.
   A variante .fx-campo--tela cobre a janela inteira (a página vai num .fx-pagina, que sobe acima dele). */
.fx-campo{
  position:absolute; inset:0; z-index:0; margin:0; overflow:hidden; contain:strict;
  pointer-events:none; user-select:none; white-space:pre;
  font-family:var(--font-code-micro); font-weight:500; font-size:14px; line-height:1.05; letter-spacing:0;
  color:var(--color-teal-texto); opacity:0; transition:opacity 1200ms var(--ease-padrao);
  -webkit-mask-image:linear-gradient(90deg,black 0,color-mix(in srgb,black 30%,transparent) 14%,color-mix(in srgb,black 14%,transparent) 50%,color-mix(in srgb,black 30%,transparent) 86%,black 100%);
          mask-image:linear-gradient(90deg,black 0,color-mix(in srgb,black 30%,transparent) 14%,color-mix(in srgb,black 14%,transparent) 50%,color-mix(in srgb,black 30%,transparent) 86%,black 100%);
}
.fx-campo.viva{ opacity:var(--fx-campo-opacidade); }
.fx-campo--tela{ position:fixed; }
.fx-pagina{ position:relative; z-index:1; }

/* ============ varredura de ignição ============ */
/* data-fx="varredura" no bloco: uma linha desce uma vez quando ele entra na tela. No <body>, cobre a janela. */
[data-fx~="varredura"]{ position:relative; overflow:hidden; overflow:clip; }
.fx-varredura{
  position:absolute; left:0; right:0; top:0; height:140px; z-index:var(--z-raised); pointer-events:none;
  background:linear-gradient(180deg,transparent,color-mix(in srgb,var(--cor-dither-1) 8%,transparent) 70%,color-mix(in srgb,var(--cor-dither-1) 42%,transparent) 98%,transparent);
  transform:translateY(-160px);
  animation:fx-varre 1250ms cubic-bezier(.6,.05,.3,1) 150ms both;
}
.fx-varredura::after{ content:""; position:absolute; left:0; right:0; bottom:1px; height:1px; background:var(--cor-dither-1); }
.fx-varredura--tela{ position:fixed; }
@keyframes fx-varre{ to{ transform:translateY(calc(var(--fx-varre-fim,100vh) + 20px)); opacity:0; } }

/* ============ fio que se desenha ============ */
/* data-fx="fio" no bloco do título: a linha de baixo se desenha quando ele chega. */
[data-fx~="fio"]::after{ content:""; display:block; height:1px; margin-top:var(--space-md); background:var(--color-teal-fio); transform-origin:0 50%; }
.fx-armado [data-fx~="fio"]::after{ transform:scaleX(0); transition:transform 1100ms var(--ease-revela) 100ms; }
.fx-armado [data-fx~="fio"].viu::after{ transform:none; }

/* ============ feixe na borda do cartão ============ */
/* Um só por página: é o único brilho do sistema. Borda de 1,5px com um arco teal que corre. */
@property --fx-ang{ syntax:"<angle>"; inherits:false; initial-value:0deg; }
@keyframes fx-feixe{ to{ --fx-ang:360deg; } }
.fx-feixe{ position:relative; }
.fx-feixe::after{
  content:""; position:absolute; inset:-1px; border-radius:inherit; padding:1.5px; pointer-events:none;
  background:conic-gradient(from var(--fx-ang),transparent 0 58%,color-mix(in srgb,var(--cor-dither-1) 55%,transparent) 78%,var(--cor-dither-1) 88%,transparent 96%);
  -webkit-mask:linear-gradient(black 0 0) content-box,linear-gradient(black 0 0);
  -webkit-mask-composite:xor;
          mask:linear-gradient(black 0 0) content-box exclude,linear-gradient(black 0 0);
  animation:fx-feixe var(--fx-feixe-duracao) linear infinite;
}
@supports (corner-shape:squircle){ .fx-feixe::after{ corner-shape:inherit; } }

/* ============ efeitos de mouse (só onde há ponteiro fino e hover) ============ */
[data-fx~="holofote"]{ position:relative; isolation:isolate; }
@media (hover:hover) and (pointer:fine){
  /* holofote: um halo teal bem leve que segue o cursor dentro do cartão */
  [data-fx~="holofote"]::before{
    content:""; position:absolute; inset:0; z-index:-1; border-radius:inherit; pointer-events:none;
    background:radial-gradient(var(--fx-holofote-raio) circle at var(--mx,50%) var(--my,50%),color-mix(in srgb,var(--cor-dither-1) 18%,transparent),transparent 65%);
    opacity:0; transition:opacity 350ms var(--ease-saida);
  }
  [data-fx~="holofote"]:hover::before{ opacity:1; }
  /* inclinação 3D: o JS escreve o transform, o CSS só dá o tempo da volta */
  [data-fx~="inclina"]{ will-change:transform; transition:transform 600ms var(--ease-revela); }
  [data-fx~="inclina"].fx-inclinando{ transition:transform 80ms linear; }
  /* botão magnético: usa a propriedade translate, pra não brigar com o encolher do aperto */
  [data-fx~="ima"]{ transition:translate 350ms var(--ease-revela),transform var(--duration-aperto) var(--ease-saida),background-color var(--duration-rapida) var(--ease-padrao); }
  [data-fx~="ima"].fx-puxa{ transition:translate 80ms linear,transform var(--duration-aperto) var(--ease-saida),background-color var(--duration-rapida) var(--ease-padrao); }
}
/* rastro: um anel que segue o cursor com atraso e glifos que caem */
.fx-anel{
  position:fixed; left:0; top:0; z-index:calc(var(--z-aviso) + 2); pointer-events:none;
  width:30px; height:30px; margin:-15px 0 0 -15px; border-radius:50%;
  border:1.5px solid color-mix(in srgb,var(--cor-dither-1) 80%,transparent); opacity:0;
  transition:width 250ms var(--ease-revela),height 250ms var(--ease-revela),margin 250ms var(--ease-revela),background-color 250ms var(--ease-padrao),opacity 300ms var(--ease-padrao);
}
.fx-anel.ativo{ opacity:1; }
.fx-anel.sobre{ width:54px; height:54px; margin:-27px 0 0 -27px; background:color-mix(in srgb,var(--cor-dither-1) 14%,transparent); }
.fx-anel.aperta{ width:22px; height:22px; margin:-11px 0 0 -11px; }
.fx-rastro{
  position:fixed; left:0; top:0; z-index:calc(var(--z-aviso) + 2); pointer-events:none;
  font-family:var(--font-code-micro); font-weight:500; font-size:14px; line-height:1; color:var(--color-teal-texto);
}

/* ============ nome que falha ============ */
/* data-fx="falha" no texto: uma falha de sinal curta, de tempos em tempos. Deslocamento, sem brilho. */
[data-fx~="falha"]{ position:relative; }
[data-fx~="falha"].fx-falha-on{ animation:fx-falha 320ms steps(2) both; }
@keyframes fx-falha{
  0%{ text-shadow:-2px 0 color-mix(in srgb,var(--cor-dither-1) 90%,transparent),2px 0 color-mix(in srgb,var(--color-tinta) 30%,transparent); clip-path:inset(0 0 55% 0); transform:translateX(2px); }
  33%{ text-shadow:2px 0 color-mix(in srgb,var(--cor-dither-1) 90%,transparent),-2px 0 color-mix(in srgb,var(--color-tinta) 30%,transparent); clip-path:inset(40% 0 0 0); transform:translateX(-3px); }
  66%{ text-shadow:-1px 0 color-mix(in srgb,var(--cor-dither-1) 75%,transparent); clip-path:inset(20% 0 30% 0); transform:translateX(1px); }
  100%{ text-shadow:none; clip-path:none; transform:none; }
}

/* ============ terminal que digita ============ */
/* <div class="fx-terminal" data-fx="terminal" data-fx-rotulo="agente" data-fx-frases="a|b|c"></div>
   Decoração de status: sem campo, sem botão. Frases de até 22 caracteres, ou cortam no celular. */
.fx-terminal{
  display:flex; align-items:center; gap:.5rem; width:100%; min-width:0; max-width:100%; min-height:2.75rem; padding:.75rem .875rem;
  border-radius:var(--radius-md); background:var(--color-canvas); box-shadow:inset 0 0 0 1px var(--cor-separador);
  font-family:var(--font-code-micro); font-weight:500; font-size:var(--text-code-micro); line-height:1.4; letter-spacing:var(--tracking-code-micro);
  color:var(--color-tinta); overflow:hidden; white-space:nowrap; user-select:none; cursor:default;
}
.fx-terminal__rot{ flex:none; color:var(--color-teal-texto); }
.fx-terminal__txt{ min-width:0; overflow:hidden; }
.fx-terminal .cursor{ flex:none; margin-left:0; }
.fx-terminal__ok{ flex:none; margin-left:auto; color:var(--color-teal-texto); opacity:0; transition:opacity 200ms var(--ease-padrao); }
.fx-terminal.feito .fx-terminal__ok{ opacity:1; }

/* ============ gráfico de barras em ASCII ============ */
/* O valor e o rótulo são texto de verdade; as barras são desenho (aria-hidden). */
.fx-grafico{ display:flex; align-items:flex-end; gap:1.5rem; min-width:0; }
.fx-col{ margin:0; display:flex; flex-direction:column; gap:.25rem; min-width:0; }
.fx-barra{
  margin:0; overflow:hidden; white-space:pre; user-select:none;
  font-family:var(--font-code-micro); font-weight:700; font-size:14px; line-height:10px; letter-spacing:0;
  color:var(--color-graf-comparacao);
}
.fx-col--dest .fx-barra{ color:var(--color-graf-destaque); }
.fx-val{ font-size:var(--text-body-small); font-weight:650; color:var(--color-tinta); font-variant-numeric:tabular-nums; }
.fx-rot{ font-size:var(--text-caption-small); color:var(--color-meta); }

/* ============ retrato e avatar que nascem em ASCII ============ */
/* A imagem precisa ser da mesma origem (servidor local, ou data URI): o canvas recusa imagem de outra origem
   e, nesse caso, a imagem aparece normal e o efeito simplesmente não roda. */
[data-fx~="retrato"],[data-fx~="avatar"]{ position:relative; overflow:hidden; }
[data-fx~="avatar"]{ border-radius:50%; }
[data-fx~="retrato"] img{ display:block; width:100%; height:100%; object-fit:cover; object-position:50% 30%; }
[data-fx~="avatar"] img{ display:block; width:100%; height:100%; object-fit:cover; }
.fx-ascii{
  position:absolute; inset:0; margin:0; overflow:hidden; pointer-events:none; user-select:none;
  display:flex; align-items:center; justify-content:center; white-space:pre;
  font-family:var(--font-code-micro); font-weight:600; letter-spacing:0;
  color:var(--color-teal-texto); background:var(--fx-ascii-fundo,var(--color-canvas));
}
[data-fx~="avatar"] .fx-ascii{ border-radius:50%; }
[data-fx~="retrato"] img{ transition:opacity 1100ms var(--ease-padrao),filter 1100ms var(--ease-padrao); }
[data-fx~="retrato"].fx-velada img{ opacity:0; filter:saturate(0) contrast(1.2); }

/* ============ dither: faixa, dissolve, rampa ============ */
/* faixa entre duas seções: <div class="fx-banda" data-fx="banda" aria-hidden="true"></div>, fora do .envolve.
   Sem script, fica um corte seco entre o tom 0 e o tom 2. */
.fx-banda{
  position:relative; display:block; height:var(--dither-banda-altura); overflow:hidden;
  background:linear-gradient(var(--cor-dither-0) 50%,var(--cor-dither-2) 50%);
}
.fx-banda canvas{ position:absolute; inset:0; width:100%; height:100%; display:block; image-rendering:pixelated; }
/* dissolve de entrada: o canvas é criado e removido pelo JS */
[data-fx~="dissolve"]{ position:relative; }
.fx-dz{ position:absolute; inset:0; width:100%; height:100%; z-index:var(--z-raised); image-rendering:pixelated; pointer-events:none; border-radius:inherit; }
/* sombra em rampa deslocada atrás de um bloco: o canvas só pinta fora do retângulo do bloco (o JS deixa o miolo transparente) */
[data-fx~="rampa"]{ position:relative; }
.fx-rampa-cv{
  position:absolute; left:0; top:0; width:100%; height:100%; image-rendering:pixelated; pointer-events:none;
  transform:translate(var(--dither-rampa-deslocamento),var(--dither-rampa-deslocamento));
}

/* ============ dither: régua, sombra do botão, ícones ============ */
/* régua pontilhada: o teal aparece só onde a máscara de 4 x 2 células deixa */
.fx-regua{
  display:block; width:100%; height:calc(var(--dither-celula) * 2); background:var(--cor-dither-1);
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='8' shape-rendering='crispEdges'%3E%3Cpath d='M0 0h4v4H0zM8 0h4v4H8zM4 4h4v4H4zM12 4h4v4h-4z'/%3E%3C/svg%3E") repeat-x 0 0 / calc(var(--dither-celula) * 4) calc(var(--dither-celula) * 2);
          mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='8' shape-rendering='crispEdges'%3E%3Cpath d='M0 0h4v4H0zM8 0h4v4H8zM4 4h4v4H4zM12 4h4v4h-4z'/%3E%3C/svg%3E") repeat-x 0 0 / calc(var(--dither-celula) * 4) calc(var(--dither-celula) * 2);
}
/* botão com sombra pontilhada deslocada: .btn.fx-sombra. No hover o botão sobe e a sombra fica onde estava;
   no aperto o botão desce até ela. A máscara tira da sombra o retângulo do próprio botão (mask-composite),
   então ela só aparece de fora, sem cobrir o rótulo. --fx-d é o deslocamento do momento. */
.fx-sombra{ --fx-d:var(--dither-sombra-deslocamento); position:relative; isolation:isolate; transition:translate 350ms var(--ease-revela),transform var(--duration-aperto) var(--ease-saida),background-color var(--duration-rapida) var(--ease-padrao); }
.fx-sombra::before{
  content:""; position:absolute; inset:0; z-index:-1; border-radius:inherit; pointer-events:none; background:var(--cor-dither-1);
  -webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' shape-rendering='crispEdges'%3E%3Cpath d='M0 0h4v4H0zM8 0h4v4H8zM4 4h4v4H4zM0 8h4v4H0zM8 8h4v4H8zM12 12h4v4h-4z'/%3E%3C/svg%3E"),linear-gradient(black 0 0);
  -webkit-mask-size:calc(var(--dither-celula) * 4) calc(var(--dither-celula) * 4),100% 100%;
  -webkit-mask-position:0 0,calc(var(--fx-d) * -1) calc(var(--fx-d) * -1);
  -webkit-mask-repeat:repeat,no-repeat;
  -webkit-mask-composite:source-out;
          mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' shape-rendering='crispEdges'%3E%3Cpath d='M0 0h4v4H0zM8 0h4v4H8zM4 4h4v4H4zM0 8h4v4H0zM8 8h4v4H8zM12 12h4v4h-4z'/%3E%3C/svg%3E"),linear-gradient(black 0 0);
          mask-size:calc(var(--dither-celula) * 4) calc(var(--dither-celula) * 4),100% 100%;
          mask-position:0 0,calc(var(--fx-d) * -1) calc(var(--fx-d) * -1);
          mask-repeat:repeat,no-repeat;
          mask-composite:subtract;
  translate:var(--fx-d) var(--fx-d);
  transition:translate 350ms var(--ease-revela),mask-position 350ms var(--ease-revela),-webkit-mask-position 350ms var(--ease-revela);
}
@media (hover:hover){
  .fx-sombra:hover{ translate:-2px -2px; --fx-d:calc(var(--dither-sombra-deslocamento) + 2px); }
}
.fx-sombra:active{ translate:3px 3px; --fx-d:calc(var(--dither-sombra-deslocamento) - 3px); }
/* ícone em pixel: SVG de sprite com shape-rendering crispEdges, na cor do texto ao redor */
.fx-ic{ display:block; flex:none; width:1.5rem; height:1.5rem; fill:currentColor; shape-rendering:crispEdges; }

/* ============ ilustração do hero com esfera viva ============ */
/* <div class="fx-arte" data-fx="esfera" data-src-claro data-src-escuro data-front-claro data-front-escuro>
   O canvas da esfera entra por baixo do <img>; o PNG "frente" é a interface sem a esfera. */
.fx-arte{ position:relative; width:100%; max-width:30rem; }
.fx-arte img{ position:relative; display:block; width:100%; height:auto; image-rendering:pixelated; }
.fx-arte .fx-esfera{ position:absolute; inset:0; width:100%; height:100%; image-rendering:pixelated; }
@media (prefers-reduced-motion:no-preference){
  @keyframes fx-pix{ from{ opacity:0; } to{ opacity:1; } }
  .fx-arte img{ animation:fx-pix 700ms steps(6) 250ms backwards; }
}

/* ============ preferências da pessoa ============ */
@media (prefers-reduced-motion:reduce){
  .fx-varredura,.fx-anel,.fx-rastro{ display:none; }
  .fx-feixe::after{ animation:none; --fx-ang:200deg; }
  .fx-armado [data-fx~="fio"]::after{ transform:none; }
  [data-fx~="retrato"].fx-velada img{ opacity:1; filter:none; }
}
@media (prefers-contrast:more){
  .fx-campo{ display:none; }
  .fx-terminal{ box-shadow:inset 0 0 0 1px var(--color-meta); }
}
@media print{ .fx-dz,.fx-campo,.fx-anel,.fx-rastro{ display:none; } }
