/* ============ páginas públicas: fachada, login e painel ============ */

body.site {
  overflow-y: auto;
  background:
    radial-gradient(1100px 620px at 12% -10%, rgba(88, 101, 242, .30), transparent 62%),
    radial-gradient(900px 620px at 92% 6%, rgba(139, 92, 246, .22), transparent 60%),
    radial-gradient(800px 500px at 60% 108%, rgba(0, 176, 244, .14), transparent 60%),
    var(--bg);
}

.wrap { width: min(1140px, 100% - 40px); margin-inline: auto; }

/* -------------------------------- topo -------------------------------- */
.nav {
  position: sticky; top: 0; z-index: 30; backdrop-filter: blur(12px);
  background: rgba(13, 14, 18, .72); border-bottom: 1px solid rgba(255, 255, 255, .06);
}
.nav .inner { display: flex; align-items: center; gap: 22px; height: 68px; }
/* A mesma arte da marca na barra do topo. flex:none pra ela não ser
   espremida quando os links do meio disputam espaço. Vem como fundo pra
   poder trocar de arquivo no tema claro do /admin: a arte é de tinta clara
   e contra o branco fica com contraste 1.33, ou seja, invisível. */
.marca-nav { display: block; flex: none; width: 152px; }
.marca-nav-img {
  display: block; width: 100%; height: 50px;
  background: url('/img/concord-marca-v4.png') left center / contain no-repeat;
}
:root[data-theme="light"] .marca-nav-img { background-image: url('/img/concord-marca-claro-v4.png'); }

/* Cartão de "Área restrita" do painel: a arte completa, centralizada. */
.marca-bloqueio {
  display: block; width: 220px; height: 82px; margin: 0 auto 16px;
  background: url('/img/concord-lockup-v4.png') center / contain no-repeat;
}
:root[data-theme="light"] .marca-bloqueio { background-image: url('/img/concord-lockup-claro-v4.png'); }
.nav .links { display: flex; gap: 22px; margin-left: 14px; font-size: 14.5px; color: var(--txt-dim); }
.nav .links a { text-decoration: none; transition: color .12s; }
.nav .links a:hover { color: var(--txt); }
.nav .grow { flex: 1; }

/* -------------------------------- herói ------------------------------- */
.hero { padding: 84px 0 40px; display: grid; grid-template-columns: 1.05fr .95fr; gap: 54px; align-items: center; }
.tagline {
  display: inline-flex; align-items: center; gap: 8px; padding: 6px 13px; border-radius: 999px;
  background: rgba(88, 101, 242, .14); border: 1px solid rgba(88, 101, 242, .35);
  font-size: 12.5px; color: #bcc3ff; margin-bottom: 20px;
}
.tagline .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--green); box-shadow: 0 0 0 4px rgba(35, 165, 90, .18); }
.hero h1 { font-size: clamp(38px, 5.2vw, 60px); font-weight: 800; letter-spacing: -.035em; }
/* mesma iridescência do anel da logo */
.hero h1 .grad {
  background: linear-gradient(110deg, #ffc7e3, #dcb8ff 34%, #a5e8f7 68%, #b8f0c6);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.hero p.lead { font-size: 17.5px; color: var(--txt-dim); margin: 20px 0 30px; max-width: 33em; }
.hero .cta { display: flex; gap: 12px; flex-wrap: wrap; }
.hero .facts { display: flex; gap: 26px; margin-top: 34px; flex-wrap: wrap; }
.hero .facts div strong { display: block; font-size: 21px; letter-spacing: -.02em; }
.hero .facts div span { font-size: 12.5px; color: var(--txt-mute); }

/* --------------------------- mockup do app ---------------------------- */
.mock {
  border-radius: 16px; overflow: hidden; border: 1px solid var(--line);
  background: var(--bg-1); box-shadow: var(--shadow); transform: perspective(1400px) rotateY(-8deg) rotateX(3deg);
}
.mock .bar { height: 34px; background: #101116; display: flex; align-items: center; gap: 6px; padding: 0 12px; border-bottom: 1px solid var(--line); }
.mock .bar i { width: 10px; height: 10px; border-radius: 50%; background: #33363f; }
.mock .body { display: grid; grid-template-columns: 60px 150px 1fr; height: 310px; }
.mock .rail { background: #0b0c10; padding: 10px 0; display: flex; flex-direction: column; align-items: center; gap: 10px; }
.mock .rail i { width: 34px; height: 34px; border-radius: 12px; background: #22242c; display: block; }
.mock .rail i.act { background: linear-gradient(135deg, var(--brand), var(--brand-2)); border-radius: 12px; }
.mock .chans { background: var(--bg-1); padding: 14px 10px; font-size: 11.5px; color: var(--txt-mute); }
.mock .chans b { display: block; text-transform: uppercase; letter-spacing: .08em; font-size: 9.5px; margin: 12px 0 7px; }
.mock .chans span { display: block; padding: 5px 8px; border-radius: 6px; color: var(--txt-dim); }
.mock .chans span.act { background: var(--bg-3); color: #fff; }
.mock .stage { background: var(--bg-2); padding: 12px; display: grid; grid-template-rows: 1fr auto; gap: 10px; }
.mock .tiles { display: grid; grid-template-columns: 1.6fr 1fr; gap: 8px; }
.mock .tiles i {
  border-radius: 10px; display: block;
  background: linear-gradient(135deg, #2b3350, #1c2030);
  border: 1px solid #2f3547;
}
.mock .tiles i.live { background: linear-gradient(135deg, #24406b, #16233c); box-shadow: inset 0 0 0 2px rgba(35, 165, 90, .5); }
.mock .dock { display: flex; gap: 7px; justify-content: center; }
.mock .dock i { width: 30px; height: 30px; border-radius: 50%; background: #2a2d36; display: block; }
.mock .dock i.red { background: var(--red); }

/* ------------------------------ recursos ------------------------------ */
section.block { padding: 64px 0; }
.section-head { text-align: center; max-width: 640px; margin: 0 auto 42px; }
.section-head h2 { font-size: clamp(26px, 3.4vw, 36px); font-weight: 800; }
.section-head p { color: var(--txt-dim); margin-top: 12px; font-size: 16px; }

.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(255px, 1fr)); gap: 16px; }
.card {
  background: linear-gradient(180deg, rgba(255, 255, 255, .045), rgba(255, 255, 255, .015));
  border: 1px solid var(--line); border-radius: 16px; padding: 24px;
  transition: transform .16s ease, border-color .16s ease;
}
.card:hover { transform: translateY(-3px); border-color: #3d4152; }
.card .ico {
  width: 42px; height: 42px; border-radius: 12px; display: grid; place-items: center; margin-bottom: 15px;
  background: rgba(88, 101, 242, .16); color: #aeb7ff;
}
.card .ico svg { width: 21px; height: 21px; }
.card h3 { font-size: 16.5px; margin-bottom: 8px; }
.card p { color: var(--txt-dim); font-size: 14.2px; }

/* ------------------------------- passos ------------------------------- */
.steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 16px; counter-reset: step; }
.step { background: var(--bg-1); border: 1px solid var(--line); border-radius: 16px; padding: 24px; position: relative; }
.step::before {
  counter-increment: step; content: counter(step);
  display: grid; place-items: center; width: 32px; height: 32px; border-radius: 10px; margin-bottom: 14px;
  background: linear-gradient(135deg, var(--brand), var(--brand-2)); font-weight: 800; font-size: 15px;
}
.step h3 { font-size: 16px; margin-bottom: 7px; }
.step p { color: var(--txt-dim); font-size: 14.2px; }

.cta-band {
  margin: 20px 0 70px; padding: 46px 32px; text-align: center; border-radius: 22px;
  border: 1px solid rgba(88, 101, 242, .35);
  background: linear-gradient(135deg, rgba(88, 101, 242, .20), rgba(139, 92, 246, .12));
}
.cta-band h2 { font-size: clamp(24px, 3.2vw, 32px); font-weight: 800; }
.cta-band p { color: var(--txt-dim); margin: 12px auto 24px; max-width: 46em; }

footer.foot {
  border-top: 1px solid var(--line); padding: 26px 0 40px; color: var(--txt-mute); font-size: 13.5px;
  display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap; align-items: center;
}

/* ------------------------------- entrar ------------------------------- */
.auth-page { min-height: 100%; display: grid; place-items: center; padding: 40px 20px; }
/* A cor sai de token em vez de um rgba fixo: o /admin usa esta mesma classe
   na tela de "Área restrita" e tem tema claro — com o escuro cravado aqui,
   lá ficava texto escuro sobre cartão escuro, ilegível. */
.auth {
  width: 100%; max-width: 440px; background: var(--bg-1); border: 1px solid var(--line);
  border-radius: 20px; padding: 30px; box-shadow: var(--shadow);
}
.auth h1 { font-size: 25px; margin-bottom: 6px; }
.auth .sub { color: var(--txt-dim); font-size: 14.5px; margin-bottom: 24px; }
.tabs { display: flex; gap: 6px; background: #0b0c10; padding: 5px; border-radius: 12px; margin-bottom: 22px; }
.tabs button {
  flex: 1; padding: 9px; border-radius: 8px; font-size: 14px; font-weight: 600; color: var(--txt-dim);
  transition: background .14s, color .14s;
}
.tabs button.on { color: var(--txt); }
.auth form { display: none; }
.auth form.on { display: block; }
.auth .foot-link { margin-top: 18px; font-size: 13px; color: var(--txt-mute); text-align: center; }
.auth .foot-link a { color: #9aa4ff; }

.notice {
  border-radius: 12px; padding: 14px 16px; font-size: 14px; line-height: 1.55;
  background: var(--chip-brand-bg); border: 1px solid rgba(88, 101, 242, .3); color: var(--chip-brand-fg);
}
.notice.good { background: var(--chip-green-bg); border-color: rgba(35, 165, 90, .35); color: var(--chip-green-fg); }
.notice code { background: rgba(127, 127, 127, .16); padding: 1px 5px; border-radius: 4px; }

/* ----------------------------- responsivo ----------------------------- */
@media (max-width: 900px) {
  .hero { grid-template-columns: 1fr; gap: 36px; padding-top: 54px; }
  .mock { transform: none; }
  .nav .links { display: none; }
  table.grid-tbl { display: block; overflow-x: auto; white-space: nowrap; }
}
@media (max-width: 380px) {
  /* Sem os links do meio sobra só marca + 2 botões, mas mesmo assim
     "Pedir acesso" estourava a barra em telas bem estreitas. A marca
     encolhe em vez de sumir: ela é a única âncora visual da barra. */
  .nav .inner { gap: 10px; }
  .marca-nav { width: 104px; }
}

/* ==================================================================
 * Refinamento das páginas públicas
 * ================================================================== */

/* ------------------------------ tipografia --------------------------- */
.hero h1 { letter-spacing: -.038em; }
.hero p.lead { font-size: var(--fs-lg); line-height: 1.55; max-width: 34ch; }
.section-head p { max-width: 52ch; margin-inline: auto; }
.hero .facts div strong { font-variant-numeric: tabular-nums; letter-spacing: -.02em; }

/* --------------------------- cartão com foco ------------------------- */
/* Brilho que segue o cursor. Combina com o anel da marca e dá a sensação de
   superfície iluminada em vez de retângulo chapado. As variáveis --mx/--my
   são atualizadas no mousemove (js/spotlight.js). */
.card {
  position: relative;
  isolation: isolate;
  box-shadow: var(--elev-1);
  transition:
    transform var(--t-medio) var(--suave),
    border-color var(--t-medio) var(--suave),
    box-shadow var(--t-medio) var(--suave);
}
.card::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  opacity: 0;
  transition: opacity var(--t-lento) var(--suave);
  background: radial-gradient(
    340px circle at var(--mx, 50%) var(--my, 50%),
    rgba(139, 92, 246, .16),
    transparent 62%
  );
}
.card:hover { transform: translateY(-3px); box-shadow: var(--elev-3); }
.card:hover::before { opacity: 1; }

/* Ícone do cartão acompanha o movimento — detalhe pequeno, muda a impressão. */
.card .ico { transition: transform var(--t-lento) var(--mola), background var(--t-medio) var(--suave); }
.card:hover .ico { transform: scale(1.06) rotate(-3deg); }

/* ------------------------------- passos ------------------------------ */
.step { transition: transform var(--t-medio) var(--suave), border-color var(--t-medio) var(--suave); }
.step:hover { transform: translateY(-2px); border-color: var(--txt-mute); }
.step::before { box-shadow: 0 6px 18px -8px rgba(88, 101, 242, .6); }

/* ------------------------------- mockup ------------------------------ */
.mock { box-shadow: var(--elev-3); transition: transform var(--t-lento) var(--suave); }
.mock:hover { transform: perspective(1400px) rotateY(-4deg) rotateX(1.5deg) translateY(-4px); }

/* ---------------------------- entrada da página ---------------------- */
/* Entrada escalonada: o olho percorre na ordem em que a informação importa. */
.hero > div > * { animation: sobe var(--t-lento) var(--suave) both; }
.hero > div > *:nth-child(2) { animation-delay: 60ms; }
.hero > div > *:nth-child(3) { animation-delay: 110ms; }
.hero > div > *:nth-child(4) { animation-delay: 160ms; }
.hero > div > *:nth-child(5) { animation-delay: 210ms; }
.hero .mock, .hero > .mock { animation: sobe var(--t-lento) var(--suave) 120ms both; }
@keyframes sobe { from { opacity: 0; transform: translateY(10px); } }

/* ==================================================================
 * Entrar — fundo vivo, cartão de vidro e microinterações
 *
 * O fundo estático de gradientes radiais lia como imagem colada. Aqui ele
 * vira três manchas de luz que derivam devagar (referência: os fundos
 * "aurora" do 21st.dev, refeitos em CSS puro — o catálogo de lá é
 * React/Tailwind e não entra num projeto sem build). Tudo é decorativo e
 * a regra global de prefers-reduced-motion no base.css congela o conjunto.
 * ================================================================== */

/* Na página de entrar o fundo do body sai de cena: quem pinta a cor agora
   são as manchas, e os dois somados viravam sopa. */
body.auth-body { background: var(--bg); }

.aurora {
  position: fixed; inset: 0; z-index: 0; overflow: hidden; pointer-events: none;
  /* Sem isto as manchas "vazam" e criam barra de rolagem em tela estreita. */
  contain: strict;
}
/* Manchas dentro da tela e com curso longo: antes elas ficavam quase todas
   fora da moldura e andavam pouco, então o fundo parecia congelado. */
.mancha { position: absolute; display: block; border-radius: 50%; will-change: transform; }
.m1 {
  width: 58vmax; height: 58vmax; top: -16vmax; left: -12vmax;
  background: radial-gradient(circle, rgba(88, 101, 242, .58), rgba(88, 101, 242, 0) 65%);
  animation: deriva-1 19s ease-in-out infinite;
}
.m2 {
  width: 52vmax; height: 52vmax; top: -9vmax; right: -13vmax;
  background: radial-gradient(circle, rgba(139, 92, 246, .52), rgba(139, 92, 246, 0) 65%);
  animation: deriva-2 24s ease-in-out infinite;
}
.m3 {
  width: 56vmax; height: 56vmax; bottom: -22vmax; left: 22vmax;
  background: radial-gradient(circle, rgba(0, 176, 244, .4), rgba(0, 176, 244, 0) 65%);
  animation: deriva-3 29s ease-in-out infinite;
}
/* Só transform e nada de left/top: a animação fica na GPU e não repinta. */
@keyframes deriva-1 {
  50% { transform: translate3d(26vmax, 16vmax, 0) scale(1.2); }
}
@keyframes deriva-2 {
  50% { transform: translate3d(-22vmax, 24vmax, 0) scale(.82); }
}
@keyframes deriva-3 {
  50% { transform: translate3d(18vmax, -26vmax, 0) scale(1.16); }
}

/* Facho de luz girando: o movimento circular passa por trás do cartão e é
   o que faz o fundo parecer papel de parede em vez de gradiente parado. */
.varal {
  position: absolute; display: block;
  /* 130vmax cobre a diagonal da tela em qualquer ângulo do giro; 200vmax
     cobria também, mas com uma textura de composição desnecessariamente
     gigante (~26 MB no desktop). */
  top: 50%; left: 50%; width: 130vmax; height: 130vmax; margin: -65vmax 0 0 -65vmax;
  background: conic-gradient(
    from 0deg,
    transparent 0 48%,
    rgba(160, 130, 255, .17) 62%,
    rgba(120, 200, 255, .1) 72%,
    transparent 84%
  );
  animation: gira-fundo 32s linear infinite;
  will-change: transform;
}
@keyframes gira-fundo { to { transform: rotate(360deg); } }

/* Malha deslizando na diagonal. Movimento com aresta definida: as manchas
   sozinhas são difusas demais pro olho registrar que algo se mexe. O passo
   da animação é exatamente um losango, então o loop não tem costura. */
.malha {
  position: absolute; display: block; inset: -12vmax;
  background-image:
    linear-gradient(rgba(255, 255, 255, .06) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, .06) 1px, transparent 1px);
  background-size: 48px 48px;
  animation: desliza-malha 12s linear infinite;
  will-change: transform;
  /* A máscara desbota nas beiradas, não no centro: com closest-side a malha
     ficava concentrada atrás do cartão — justamente onde ninguém a vê — e
     sumia nas laterais, que é a área de fundo que sobra na tela. */
  -webkit-mask-image: radial-gradient(140% 110% at 50% 45%, rgba(0, 0, 0, .9), rgba(0, 0, 0, .4) 62%, transparent 100%);
  mask-image: radial-gradient(140% 110% at 50% 45%, rgba(0, 0, 0, .9), rgba(0, 0, 0, .4) 62%, transparent 100%);
}
@keyframes desliza-malha { to { transform: translate3d(48px, 48px, 0); } }

/* Véu: escurece de forma quase uniforme, só pra tirar o exagero das cores.
   A versão anterior era um vinhete forte no centro e matava exatamente o
   movimento que aparece em volta do cartão. */
.veu {
  position: absolute; display: block; inset: 0;
  background: radial-gradient(
    130% 100% at 50% 40%,
    rgba(8, 9, 13, .5) 0%,
    rgba(8, 9, 13, .34) 52%,
    rgba(8, 9, 13, .5) 100%
  );
}

/* Grão: quebra o degradê perfeito demais e mata o banding em tela de 8 bits. */
.grao {
  position: fixed; inset: 0; z-index: 1; pointer-events: none;
  opacity: .05; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/></filter><rect width='140' height='140' filter='url(%23n)'/></svg>");
}

/* ------------------------------- cartão ------------------------------ */
.auth-page { position: relative; z-index: 2; }

.auth-body .auth {
  position: relative;
  isolation: isolate;
  /* Fundo em três camadas: brilho que segue o cursor, verniz de cima pra
     baixo e a base translúcida que deixa a aurora atravessar. */
  background:
    radial-gradient(460px circle at var(--mx, 50%) var(--my, -20%), rgba(139, 92, 246, .1), transparent 62%),
    linear-gradient(180deg, rgba(255, 255, 255, .028), rgba(255, 255, 255, .005)),
    rgba(8, 9, 12, .94);
  backdrop-filter: blur(26px) saturate(1.1);
  -webkit-backdrop-filter: blur(26px) saturate(1.1);
  border-color: rgba(255, 255, 255, .08);
  box-shadow: var(--elev-3), 0 0 70px -18px rgba(88, 101, 242, .5);
  animation: modal-entra var(--t-lento) var(--mola);
}

/* Cartão quase preto: os campos não podem continuar mais escuros que ele
   (--bg-void é praticamente a mesma cor) ou somem. Aqui eles sobem em vez
   de afundar — luz baixa por cima do cartão, borda visível. */
.auth-body .field {
  background: rgba(255, 255, 255, .04);
  border-color: rgba(255, 255, 255, .1);
}
.auth-body .field:hover:not(:focus) { border-color: rgba(255, 255, 255, .2); }
.auth-body .field:focus { background: rgba(255, 255, 255, .06); border-color: var(--brand); }
/* O poço das abas afunda pelo preto, não por uma cor fixa que virou igual
   à do cartão. */
.auth-body .tabs { background: rgba(0, 0, 0, .4); }

/* Aro iridescente girando por fora da borda. @property é o que permite
   animar o ângulo do conic-gradient; onde não houver, o aro simplesmente
   fica parado — nada quebra. */
@property --giro {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}
/* z-index negativo NÃO joga o pseudo pra trás do fundo do cartão: na ordem
   de pintura, filhos com z-index negativo entram depois do background do
   elemento que criou o contexto de empilhamento. Sem recortar, este conic
   preenchia o cartão inteiro de roxo — era a "placa clara". As duas máscaras
   com mask-composite: exclude vazam só a faixa do padding, deixando apenas
   o aro de 2px em volta da borda. */
.auth-body .auth::before {
  content: '';
  position: absolute; inset: -2px; z-index: -1;
  border-radius: inherit;
  padding: 2px;
  background: conic-gradient(
    from var(--giro),
    rgba(88, 101, 242, .9),
    rgba(139, 92, 246, .65) 25%,
    rgba(0, 176, 244, .5) 50%,
    rgba(139, 92, 246, .65) 75%,
    rgba(88, 101, 242, .9)
  );
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  animation: girar 9s linear infinite;
  opacity: .8;
}
@keyframes girar { to { --giro: 360deg; } }

/* Erro de credencial: o cartão nega com a cabeça. */
.auth-body .auth.treme { animation: treme .5s var(--suave); }
@keyframes treme {
  10%, 90% { transform: translateX(-2px); }
  20%, 80% { transform: translateX(4px); }
  30%, 50%, 70% { transform: translateX(-7px); }
  40%, 60% { transform: translateX(7px); }
}

/* -------------------------------- abas ------------------------------- */
/* A pastilha desliza entre as duas abas em vez de aparecer do nada. */
.tabs { position: relative; }
.tabs::before {
  content: '';
  position: absolute; top: 5px; bottom: 5px; left: 5px;
  width: calc(50% - 8px);
  border-radius: 8px;
  background: var(--bg-3);
  box-shadow: var(--elev-1);
  transition: transform var(--t-medio) var(--mola);
}
.tabs.dois::before { transform: translateX(calc(100% + 6px)); }
.tabs button {
  position: relative; z-index: 1; background: transparent;
  transition: color var(--t-medio) var(--suave);
}

/* Painel que entra vem do lado da aba clicada — o movimento explica de
   onde a informação veio. */
.auth form.on { animation: aba-entra var(--t-medio) var(--suave); }
#formLogin.on { animation-name: aba-esq; }
#formRegister.on { animation-name: aba-dir; }
@keyframes aba-entra { from { opacity: 0; transform: translateY(6px); } }
@keyframes aba-esq { from { opacity: 0; transform: translateX(-12px); } }
@keyframes aba-dir { from { opacity: 0; transform: translateX(12px); } }

/* ------------------------------- campos ------------------------------ */
.campo { position: relative; }
.campo .field { padding: 22px 14px 7px; }
/* Campo com botão do olho: o texto não pode correr por baixo dele. */
.campo:has(.olho) .field { padding-right: 46px; }

.campo .lbl-flut {
  position: absolute; left: 15px; top: 15px;
  font-size: var(--fs-base); line-height: 1.5; color: var(--txt-mute);
  pointer-events: none; transform-origin: 0 0;
  transition: transform var(--t-medio) var(--suave), color var(--t-medio) var(--suave);
}
/* :placeholder-shown é o que detecta "campo vazio" sem depender de JS — por
   isso todo input aqui usa placeholder=" ". O :autofill cobre o preenchido
   pelo navegador, que não dispara evento nenhum. */
.campo .field:focus + .lbl-flut,
.campo .field:not(:placeholder-shown) + .lbl-flut,
.campo .field:autofill + .lbl-flut {
  transform: translateY(-9px) scale(.72);
  color: var(--txt-dim);
}
.campo .field:focus + .lbl-flut { color: #aeb7ff; }

.campo .olho {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  width: 34px; height: 34px; border-radius: 8px; display: grid; place-items: center;
  color: var(--txt-mute);
  transition: color var(--t-rapido) var(--suave), background var(--t-rapido) var(--suave);
}
.campo .olho:hover { color: var(--txt); background: var(--hover-tint); }
.campo .olho svg { width: 18px; height: 18px; }

/* Campo recusado pelo servidor: vermelho até a pessoa mexer nele de novo. */
.field.ruim { border-color: var(--red); box-shadow: 0 0 0 3px rgba(242, 63, 67, .16); }
.field.ruim + .lbl-flut { color: #ff9294; }

.hint code { background: rgba(127, 127, 127, .16); padding: 1px 5px; border-radius: 4px; font-size: .95em; }

/* ------------------------------- a marca ----------------------------- */
/* A arte já traz mascote, wordmark e lema juntos — por isso aqui não há
   texto de marca nenhum, só a imagem. */
.marca {
  display: block;
  width: min(248px, 68%);
  margin-bottom: 24px;
}
.marca img { display: block; width: 100%; height: auto; }

/* ---------------------------- botão de ação --------------------------- */
/* Três estados no mesmo botão: rótulo, girando e concluído. Sem trocar o
   tamanho — trocar largura no meio do envio faz a página pular. */
.btn.acao { position: relative; overflow: hidden; }
.btn.acao .rotulo { transition: opacity var(--t-rapido) var(--suave), transform var(--t-rapido) var(--suave); }
.btn.acao .giro, .btn.acao .ok {
  position: absolute; inset: 0; margin: auto;
  opacity: 0; transition: opacity var(--t-rapido) var(--suave);
}
.btn.acao .giro {
  width: 18px; height: 18px; border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, .3); border-top-color: #fff;
  animation: rodar .7s linear infinite;
}
.btn.acao .ok {
  width: 21px; height: 21px;
  fill: none; stroke: #fff; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round;
}
.btn.acao .ok path { stroke-dasharray: 24; stroke-dashoffset: 24; }

.btn.acao.carregando .rotulo { opacity: 0; transform: translateY(-7px); }
.btn.acao.carregando .giro { opacity: 1; }
.btn.acao.pronto .rotulo { opacity: 0; }
.btn.acao.pronto .ok { opacity: 1; }
.btn.acao.pronto .ok path { animation: risca .34s var(--suave) forwards; }
.btn.acao.pronto { background: var(--green); }
/* Desabilitar é o que impede o clique duplo, mas apagar o botão enquanto
   ele mostra progresso passaria a ideia errada. */
.btn.acao.carregando:disabled, .btn.acao.pronto:disabled { opacity: 1; cursor: default; }

/* Brilho varrendo o botão no hover — o degradê parado é a cara de template. */
.btn.primary.acao::after {
  content: '';
  position: absolute; top: 0; bottom: 0; width: 45%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .22), transparent);
  transform: translateX(-170%);
  pointer-events: none;
}
.btn.primary.acao:hover::after { animation: varre .75s var(--suave); }
.btn.acao.carregando::after, .btn.acao.pronto::after { display: none; }

@keyframes rodar { to { transform: rotate(360deg); } }
@keyframes risca { to { stroke-dashoffset: 0; } }
@keyframes varre { to { transform: translateX(350%); } }
