/* ==========================================================================
   SOFIA — SISTEMA VISUAL  ·  web/assets/tokens.css  ·  fonte única de verdade
   Tese: chassis escuro e mudo; a única luz, cor e movimento é o som.
   Regra dura: só dois objectos preenchidos a teal — o orbe e o botão que
   fala com ele. Sem dourado. Sem rosa. Sem glow. Sem blur > 4px.
   Cada token diz o PAPEL que ocupa, não o tom que tem.

   ESTE FICHEIRO É LIDO, NUNCA COPIADO.
   · as quatro páginas de web/ fazem <link rel="stylesheet" href="/assets/tokens.css">
   · app.py lê-o no import do módulo e inlina-o nas páginas breves (404/410/opt-out),
     que são de uma só vista e não podem ficar à espera da rede para pintar.
   Cinco :root sincronizados à mão foi como o dourado sobreviveu a 51 sítios.
   ========================================================================== */
:root {

  /* ═══ 1. CHASSIS — superfícies por ordem de elevação ═══════════════════
     Degraus minúsculos (delta 9–14), à maneira do fin/pricing (#010411 →
     #080C19, 7 unidades e uma linha). Num ecrã escuro a sombra não se vê:
     a profundidade vem do delta de superfície somado à hairline. Cada
     degrau tem UM papel — o fundo nunca é cartão, o cartão nunca é campo. */
  --fundo:      #041416;  /* chão da página. Nenhuma secção troca de fundo por gosto. */
  --sup-1:      #071d20;  /* superfície pousada: banda de secção alternada, painel calmo. */
  --sup-2:      #0a262a;  /* superfície elevada: cartão do palco, cartão de plano, cartão de prova. */
  --sup-alta:   #103033;  /* o MESMO objecto em hover/seleccionado. É estado, não cor nova. */
  --recesso:    #010f11;  /* superfície RECUADA: campo de formulário, bloco de código, calha
                             de tabs. Mais ESCURO que o chão — lê-se como inset, não como caixa. */
  --veu:        rgba(2,10,11,.72);  /* véu sobre o conteúdo quando um painel modal abre. */

  /* ═══ 2. TINTA — quatro degraus e mais nenhum ══════════════════════════
     Texto que não cabe num destes papéis não devia estar na página.
     Verificados contra TODAS as superfícies em que podem aterrar, não só
     contra o chão (ver auditoria em §2.1 do SISTEMA.md). */
  --txt:        #e9f1f0;  /* o que se lê a sério: display, títulos, corpo, número-prova. */
  --txt-2:      #9db3b3;  /* apoio: lead, descrições, legendas, parágrafos secundários. */
  --txt-3:      #879d9e;  /* metainformação mono, microcópia, marcadores, rodapés.
                             NOTA: é #879d9e e não #7b9293 de propósito — o valor mais escuro
                             caía a 4,27:1 sobre --sup-alta. Este passa AA em TODAS as cinco. */
  --txt-inv:    #041416;  /* tinta sobre preenchimento teal ou claro (rótulo do botão primário). */

  /* ═══ 3. VOZ — o único sítio onde o croma ocupa ÁREA ═══════════════════
     A rampa iluminada do orbe: alto-luz num canto, terminador no oposto.
     É isto — mais o grão — que separa um objecto de um radial-gradient.
     Regra dura: se não é som, não leva estas cores. */
  --voz-luz:    #b8f5ea;  /* alto-luz do orbe, canto superior-esquerdo. */
  --voz-meio:   #5fe0cd;  /* meio-tom da esfera; picos da onda enquanto a Sofia fala. */
  --voz-nucleo: #00b2a4;  /* corpo da esfera. É LITERALMENTE --accao, e isso é a tese. */
  --voz-term:   #0a5f5c;  /* terminador do orbe, inferior-direito, onde a luz morre. */
  --voz-vidro:  rgba(95,224,205,.10); /* halo interno do disco de microfone. Só DENTRO do orbe. */
  --vivo:       #7fdcd3;  /* estado NO AR: ponto pulsante, moldura do palco, anel de foco. */
  --onda-quieta: rgba(135,157,158,.38); /* barras da onda em silêncio — NEUTRAS, não teal. */
  --onda-viva:  var(--voz-meio);        /* barras da onda enquanto há sinal de microfone. */
  --grao:       .045;     /* opacidade do ruído feTurbulence sobre a PÁGINA (body::before). */
  --grao-orbe:  1;        /* o MESMO ruído DENTRO do orbe — o degrau que dá matéria à esfera.
                             É este número, e nenhum outro, que o teste de matéria (§6.1 do
                             SISTEMA) afina, e afina-se AQUI: vivia cravado a .55 no HTML da
                             vertical, e quem lesse este ficheiro não percebia porque é que
                             o orbe não era liso. Medido em janelas de 24px dentro do disco,
                             o desvio-padrão médio por canal dá 3,1 sem grão nenhum (é a
                             rampa), 3,2 com o .09 que o documento adiantava — ou seja, o
                             .09 não acrescenta matéria — e 6,6 aqui, dentro da banda 6–10
                             que o teste exige. Se for preciso mais, o passo seguinte é o
                             plano B declarado: PNG de ruído pré-renderizado em repeat. */
  --orbe-inerte:.78;      /* opacidade do orbe no estado de fallback (microfone negado). */

  /* ═══ 4. ACÇÃO — dois níveis, nunca três ══════════════════════════════
     O teal só pinta aquilo em que se clica. Não pinta bullets, vistos,
     sublinhados de ênfase, selos, numerais, ícones nem itálicos. */
  --accao:       #00b2a4;              /* preenchimento do primário e do disco de falar. 7,08:1. */
  --accao-hover: #14c4b6;              /* o mesmo botão sob o cursor. */
  --accao-viva:  #00a294;              /* o mesmo botão premido (:active). */
  --accao-tenue: rgba(0,178,164,.14);  /* fundo de estado ligado/seleccionado, sem virar botão. */
  --accao-txt:   var(--txt-inv);       /* rótulo dentro do primário. */
  --ctl-2-bd:    var(--linha-forte);   /* borda 1px do secundário; fundo transparente. */
  --foco:        var(--vivo);          /* anel de foco de teclado: 2px sólido + 2px offset. */

  /* ═══ 5. RÉGUA — a arquitectura invisível ═════════════════════════════
     Substitui por inteiro a hairline dourada rgba(216,189,143,.18).
     Três forças com papéis distintos: a régua que atravessa a página toda
     e mal se vê; o traço que desenha um objecto; a aresta que TEM de se
     ver porque é onde se escreve ou se clica. */
  --regua:       rgba(233,241,240,.05);  /* réguas verticais permanentes do contentor. */
  --linha:       rgba(233,241,240,.085); /* fronteira de secção full-bleed; aresta de cartão. */
  --linha-forte: rgba(233,241,240,.18);  /* aresta de campo e de botão secundário. */
  --regua-traco: 6px 6px;                /* dash das réguas que se PROLONGAM para fora do cartão.
                                            É isto, e não sombra, que ancora os cartões do
                                            fin/pricing. Mata o cartão-ilha de 1080×673 do index. */

  /* ═══ 6. SINAL — estado, nunca decoração ══════════════════════════════
     Uma só tinta de alarme. Terracota e não coral: está mais longe da
     matiz do dourado que acabámos de cortar (~38°) do que qualquer dos
     dois corais propostos, e portanto não o evoca. Se aparecer num
     título, numa hairline, num bullet ou num selo, o dourado voltou com
     outro código hexadecimal e este token sai. */
  --erro:        #e4705c;               /* 6,06:1 sobre --fundo; 5,12:1 sobre --sup-2. */
  --erro-fundo:  rgba(228,112,92,.10);  /* lavagem de campo inválido e faixa de erro. */
  --erro-linha:  rgba(228,112,92,.34);  /* aresta de campo inválido. */
  --ok:          var(--vivo);           /* sucesso NÃO estreia cor: é o realce que já existe. */

  /* ═══ 7. FAMÍLIAS — três vozes, papéis estanques ══════════════════════
     serif = o que se afirma · sans = o que se lê · mono = o que se verifica.
     A mono é stack de sistema: zero custo de rede, e as strings são curtas
     e em maiúsculas, onde SF Mono e Consolas não se distinguem. */
  --serif: "Fraunces", Georgia, "Times New Roman", serif;
  --sans:  "Instrument Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --mono:  ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace;

  /* Eixos da Fraunces. WONK 0 tira-lhe o floreado de convite de casamento;
     opsz alto aperta o desenho e afina a haste no tamanho grande. */
  --serif-opsz-xl: "opsz" 120, "SOFT" 0, "WONK" 0;  /* display */
  --serif-opsz-md: "opsz" 48,  "SOFT" 0, "WONK" 0;  /* h2, h3 */
  --serif-opsz-num:"opsz" 84,  "SOFT" 0, "WONK" 0;  /* NÚMEROS: --fs-numeral e --fs-num.
                             A regra é simples — a Fraunces a fazer LETRAS de display usa
                             o opsz-xl; a fazer NÚMEROS usa este. A 132px o opsz 120 leva
                             a diagonal e o travessão do «4» a espessura sub-pixel: a 1x
                             serrilham ou desaparecem, que é exactamente a falha que a
                             regra do peso 300 (§3.1) existe para evitar — só que entrava
                             pela porta do eixo óptico. O opsz mais baixo é o desenho mais
                             robusto da Fraunces: engrossa a haste fina sem estrear peso. */
  --num-tab: "tnum" 1, "lnum" 1;  /* numerais tabulares: preços e ROI alinham em coluna. */

  /* ═══ 8. ESCALA TIPOGRÁFICA — nove degraus, de 390px a 1440px ═════════
     Verificados à mão nos dois extremos. Não existe nada entre estes
     degraus: se um texto precisa de um tamanho novo, o problema é de
     hierarquia, não de escala. */
  --fs-numeral:  clamp(80px, 4.95vw + 60.7px, 132px); /* numeral das breves e ROI de fecho */
  --fs-display:  clamp(56px, 3.81vw + 41.1px,  96px); /* h1 SEM palco no fold — a reserva de impacto */
  --fs-display-palco: clamp(46px, 2.48vw + 36.3px, 72px); /* h1 COM palco no fold (vertical) */
  --fs-num:      clamp(44px, 2.67vw + 33.6px,  72px); /* o número que é argumento: 190 €, ROI, taxa */
  --fs-h2:       clamp(30px, 1.52vw + 24.1px,  46px); /* título de secção */
  --fs-h3:       clamp(21px, 0.48vw + 19.1px,  26px); /* título de cartão; frase da breve */
  --fs-lead:     clamp(18px, 0.29vw + 16.9px,  21px); /* o parágrafo que segue o display */
  --fs-corpo:    clamp(17px, 0.10vw + 16.6px,  18px); /* corpo corrido */
  --fs-micro:    15px;  /* PISO ABSOLUTO de texto corrido. Legal e consentimento vivem aqui. */
  --fs-ctl:      16px;  /* rótulo de botão e de campo. Igual em desktop e mobile. */
  --fs-mono:     12px;  /* etiqueta mono. Só strings curtas, isoladas, sempre em maiúsculas. */

  --lh-numeral: .92;  --tr-numeral: -.03em;
  --lh-display: .98;  --tr-display: -.028em;
  --lh-num:    1;     --tr-num:     -.03em;
  --lh-h2:     1.08;  --tr-h2:      -.018em;
  --lh-h3:     1.28;  --tr-h3:      -.008em;
  --lh-lead:   1.45;
  --lh-corpo:  1.55;  --tr-corpo:    0;
  --lh-micro:  1.45;
  --lh-mono:   1.35;  --tr-mono:     .12em;

  /* Pesos — o 300 NÃO existe no sistema. A 46–96px de Fraunces sobre fundo
     escuro, a haste de 300 serrilha ou desaparece a 1x. */
  --p-display: 400;  --p-corpo: 400;  --p-medio: 500;  --p-forte: 600;

  /* Medida de leitura — a metade não ocupada por texto é imagem ou é vazio. */
  --medida:       62ch;  /* corpo corrido. Nunca mais larga. */
  --medida-lead:  30ch;  /* lead do herói. */
  --medida-h1:    15ch;  /* por linha de display — é o que força as 2–3 linhas. */
  --medida-larga: 78ch;  /* apenas tabelas e blocos de dados. */

  /* ═══ 9. ESPAÇO — base 4, onze degraus, nada fora daqui ═══════════════ */
  --e-1:  4px;  --e-2:  8px;  --e-3: 12px;  --e-4: 16px;
  --e-5: 24px;  --e-6: 32px;  --e-7: 48px;  --e-8: 64px;
  --e-9: 96px;  --e-10:128px; --e-11:160px;

  /* Padding de secção ALTERNADO — dois tokens, não prosa. Secções todas
     com a mesma altura lêem-se como rolo; alternar dá cadência.
     Os TECTOS (160 / 96) são os medidos contra a fasquia a 1440 e não se
     mexem; os PISOS descem para os degraus 48 e 32 da escala. A 390px o
     par 72+72 dava 144px de vazio entre dois argumentos, mais 65px de
     cabeçalho: meio ecrã sem tinta, que em mobile não se lê como cadência,
     lê-se como falha de render (a fasquia faz 56–80px). Agora a transição
     típica (argumento → friso) é 48+32 = 80px, o topo dessa banda. */
  --sec-pad:   clamp(48px, 10.67vw + 6.4px, 160px); /* secção de argumento */
  --sec-pad-s: clamp(32px,  6.10vw + 8.2px,  96px); /* friso, prova, fecho */
  --e-bloco:   clamp(32px, 3.05vw + 20.1px,  64px); /* entre blocos dentro da secção */

  /* Ritmo vertical NOMEADO. Com HTML emitido por Python, prosa não é
     contrato: os fragmentos do proposta.py não conseguem derivar "24/24/48". */
  --rit-label-h2:   var(--e-5);  /* etiqueta mono → h2 ............ 24 */
  --rit-h2-lead:    var(--e-5);  /* h2 → lead ..................... 24 */
  --rit-lead-corpo: var(--e-7);  /* lead → primeiro bloco ......... 48 */
  --rit-bloco:      var(--e-7);  /* bloco → bloco ................. 48 */
  --rit-para:       var(--e-5);  /* parágrafo → parágrafo ......... 24 */
  --rit-h1-cta:     var(--e-6);  /* display → par de botões ....... 32 */
  --rit-cta-micro:  var(--e-4);  /* botão → a ÚNICA linha de micro . 16 */

  /* ═══ 10. GRELHA ══════════════════════════════════════════════════════
     A goteira é HONESTA: é padding real, não a margem calculada do
     contentor centrado. A 1440 o contentor a 1160 deixa 140px por lado
     (9,7%) — a fasquia da elevenlabs é 9,1%. Deixa o max-width fazer o
     resto; nunca escrevas padding:0 clamp(...,131px). */
  --container: 1160px;
  --gutter:    clamp(20px, 4.2vw, 32px);
  --regua-w:   1px;  /* espessura de toda e qualquer hairline. Nunca 2px. */

  /* ═══ 11. CONTROLOS — altura igual em desktop e mobile ════════════════ */
  --ctl-h:    48px;  /* botão */
  --campo-h:  48px;  /* campo de formulário */
  --ctl-px:   24px;
  --ctl-gap:   8px;  /* entre primário e secundário */
  --alvo:     44px;  /* alvo de toque mínimo. Nada abaixo disto é clicável. */
  --caixa-marca: 20px; /* quadrado desenhado do checkbox, dentro de uma caixa de --alvo */

  /* ═══ 12. O INSTRUMENTO — geometria do palco de voz ═══════════════════
     O objecto mais importante do sistema TEM geometria tokenizada — não
     números em prosa num mapa de fold. */
  --orbe:     256px;  /* diâmetro em desktop */
  --orbe-m:   208px;  /* diâmetro em mobile — continua acima da dobra */
  --mic:       56px;  /* disco de microfone/play ao centro: 22% do orbe, como a fasquia */
  --palco-h:  460px;  /* altura do palco em desktop — começa a 480 e é CORTADO pela dobra */
  --palco-h-m:436px;  /* idem em mobile, a começar a 408 */

  /* ═══ 13. RAIOS ═══════════════════════════════════════════════════════ */
  --r-cartao: 20px;  --r-campo: 12px;  --r-chip: 8px;  --r-pilula: 999px;  --r-tick: 2px;

  /* ═══ 14. PROFUNDIDADE ════════════════════════════════════════════════
     Duas sombras e o tecto é a segunda. NÃO existe token de glow: foi
     retirado de propósito, e com ele morre o halo do CTA teal actual.
     Tudo o resto eleva-se com superfície + linha. */
  --sombra-1: 0 1px 2px rgba(0,0,0,.38), 0 1px 3px rgba(0,0,0,.20);
  --sombra-2: 0 2px 4px rgba(0,0,0,.42), 0 1px 2px rgba(0,0,0,.24);

  /* ═══ 15. MOTION — só se move o que o som justifica ═══════════════════ */
  --d-estado:  120ms;  /* cor de controlo: hover, foco, activo */
  --d-instr:   180ms;  /* o instrumento a mudar de estado (repouso ⇄ no ar) */
  --d-linha:   200ms;  /* uma linha de transcrição a entrar porque alguém falou */
  --d-painel:  320ms;  /* painel/acordeão a abrir */
  --d-medidor:  60ms;  /* suavização do analisador de amplitude. LINEAR, sempre. */
  --orbe-giro:      24s; /* rotação interna do gradiente em repouso */
  --orbe-giro-vivo: 16s; /* a MESMA rotação, mais depressa, enquanto ouve.
                            O estado exprime-se em TEMPO, não em cor nem em escala. */
  --e-saida:     cubic-bezier(.2,0,0,1);     /* sai depressa, assenta */
  --e-suave:     cubic-bezier(.2,.8,.2,1);   /* entrada de conteúdo diegético */
  --e-simetrica: cubic-bezier(.65,0,.35,1);  /* travessias simétricas */

  /* ═══ 16. CAMADAS ═════════════════════════════════════════════════════ */
  --z-cartao: 1;  --z-barra: 50;  --z-painel: 80;  --z-modal: 100;
}

/* Congela o sistema para quem pediu menos movimento. Nada desaparece,
   nada muda de layout, nada fica escondido — só pára. O medidor de
   amplitude SOBREVIVE: é indicador funcional ("estou a ouvir-te"), não
   teatro, e retirá-lo tira feedback a quem mais precisa dele. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --d-estado:1ms; --d-instr:1ms; --d-linha:1ms; --d-painel:1ms;
    --orbe-giro:0s; --orbe-giro-vivo:0s;
  }
  *, *::before, *::after {
    animation-duration:1ms !important;
    animation-iteration-count:1 !important;
    transition-duration:1ms !important;
    scroll-behavior:auto !important;
  }
  .onda span { transition: transform var(--d-medidor) linear !important; } /* excepção deliberada */
}

/* ══════════════════════════════════════════════════════════════════════
   BASE PARTILHADA — o mínimo que TODAS as páginas precisam de ter igual.
   Não é folha de componentes: é o chão. Regras de página vivem na página.
   ══════════════════════════════════════════════════════════════════════ */

/* Reset mínimo. O box-sizing é global porque uma página que o esqueça mede
   os campos ao contrário de todas as outras. */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; }
body {
  background: var(--fundo);
  color: var(--txt);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
img, svg, video, canvas { max-width: 100%; }

/* A selecção usa a tinta da acção — é o mesmo teal, e é o único sítio fora
   do orbe e do primário onde ele preenche área, porque é o utilizador que o
   pinta, não nós. */
::selection { background: var(--accao); color: var(--accao-txt); }

/* O LINK É UM SÓ EM TODO O SITE. Estava a ser duas coisas: mint com sublinhado
   nas landings, --txt com sublinhado a --txt-3 nas breves e na privacidade. Em
   mobile, onde o link é a navegação, a mesma affordance mudava de aparência de
   página para página — e a variante mint contradizia a regra "nenhuma tinta de
   texto fora de --txt / --txt-2 / --txt-3" (o croma pertence ao som).
   O que faz o link é o SUBLINHADO, e é ele que acende no hover.
   Um controlo (.ctl) não é um link: repõe a sua própria tinta e tira o traço. */
a { color: var(--txt); text-decoration: underline;
    text-decoration-color: var(--txt-3);
    text-decoration-thickness: var(--regua-w);  /* o sublinhado é uma hairline */
    text-underline-offset: 3px;
    transition: text-decoration-color var(--d-estado); }
a:hover { text-decoration-color: var(--txt); }

/* Anel de foco: 2px sólido, offset 2px, SEM transição — um anel de foco que
   desliza é um anel de foco que se perde. Vale para todo e qualquer controlo. */
:focus-visible {
  outline: 2px solid var(--foco);
  outline-offset: 2px;
  transition: none;
}

/* ══════════════════════════════════════════════════════════════════════
   O CONTROLO — o único componente que este ficheiro tem, e tem-no por
   necessidade: o CTA é o objecto de que depende o negócio inteiro e estava
   escrito à mão cinco vezes, em quatro vocabulários (.ctl/.ctl-1/.ctl-2,
   .btn/.btn.ghost, .btn/.btn-1/.btn-2, .btn/.ghost). Já tinham divergido —
   line-height 1 contra 1.15, `height` fixo com nowrap (um rótulo que quebre
   transborda em vez de crescer) contra `min-height`, goteira de 16px contra
   os 24 de --ctl-px. Cinco cópias de um objecto é como o dourado sobreviveu
   a 51 sítios; os tokens de geometria já viviam aqui, faltava o objecto.
   Dois níveis e nunca três: preenchido e a contorno. O que sobra é um link.
   ══════════════════════════════════════════════════════════════════════ */
.ctl {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--e-2);
  /* min-height e não height: um rótulo que quebre CRESCE, não transborda. */
  min-height: var(--ctl-h); padding-inline: var(--ctl-px);
  /* a borda existe sempre, transparente no primário: sem ela os dois níveis
     têm alturas diferentes de 2px e não alinham na mesma fila. */
  border: var(--regua-w) solid transparent; border-radius: var(--r-pilula);
  font: var(--p-medio) var(--fs-ctl)/1.15 var(--sans); letter-spacing: .005em;
  text-align: center; text-decoration: none; cursor: pointer;
  transition: background-color var(--d-estado), border-color var(--d-estado),
              color var(--d-estado);
}
/* O primário é teal porque o primário É o controlo de voz: o que se carrega
   tem a cor do que responde. Sem sombra, sem glow, sem transform. */
.ctl-1 { background: var(--accao); color: var(--accao-txt); border-color: var(--accao); }
.ctl-1:hover:not(:disabled)  { background: var(--accao-hover); border-color: var(--accao-hover); }
.ctl-1:active:not(:disabled) { background: var(--accao-viva);  border-color: var(--accao-viva); }
.ctl-2 { background: transparent; color: var(--txt); border-color: var(--ctl-2-bd); }
.ctl-2:hover:not(:disabled) { background: var(--accao-tenue); }
.ctl:disabled { opacity: .55; cursor: not-allowed; }
.ctl svg { flex: none; }

/* ══════════════════════════════════════════════════════════════════════
   A RÉGUA TRACEJADA HORIZONTAL — o mecanismo, não só o valor.
   --regua-traco só é consumível por background-size; um `border-style:
   dashed` ignora o token em silêncio e herda o passo do browser, e foi assim
   que a régua das breves deixou de bater certo com as réguas do palco.
   Aplica-se à aresta de TOPO do bloco que já existe (não precisa de elemento
   novo), e funciona como filete solto porque traz a espessura mínima.
   ══════════════════════════════════════════════════════════════════════ */
.traco-h {
  min-height: var(--regua-w);
  background-image: linear-gradient(90deg, var(--linha) 50%, transparent 0);
  background-size: var(--regua-traco); background-repeat: repeat-x;
  background-position: left top;
}
