/* ======================================================================
   CENA 04 — "O sinal torna-se sistema", Fase 1: Prólogo + Actos 01 e 02.
   Ver `ROTEIRO-SECAO04-CINEMATICA-20260925.md`.

   TODO O ESTADO É FUNÇÃO DE `--i`, E NADA MAIS.
   `--i` é o progresso contínuo das estações (0..3) que o `path.js` já
   escrevia em `.path` — não foi acrescentado nenhum motor, nenhum
   temporizador e nenhum segundo laço de render. Isto não é elegância: é a
   única forma de a cena se desfazer ao subir o scroll exactamente como se
   fez ao descer, que é um critério do roteiro, e de continuar a funcionar
   quando o `snap` do `path.js` puxa a pista para uma estação inteira.

   Por isso também NÃO HÁ JAVASCRIPT NOVO. Cada progresso local sai de um
   `clamp()` sobre `--i`; cada elemento que precisa de desfasamento traz o
   seu `--dl` inline no HTML. `calc()` sabe recuar sozinho.

   `--i` NÃO EXISTE EM COLUNA NEM EM MOVIMENTO REDUZIDO. `path.js` só o
   escreve no `runMode()` (>=900px, sem `prefers-reduced-motion`), e a
   limpeza do modo faz `removeProperty('--i')`. Verificado no ficheiro, não
   deduzido. Todas as leituras trazem por isso um valor de recurso que é
   exactamente o ESTADO RESOLVIDO do acto — é assim que o roteiro pede que
   o movimento reduzido e o retrato se vejam: composições estáticas
   completas, sem pin e sem animação contínua.
   ====================================================================== */

/* ---- progresso local de cada acto --------------------------------------
   Acto 01 vive em `--i` 0..1, Acto 02 em 1..2. O valor de recurso (1 e 2)
   entrega o acto já resolvido a quem não tem `--i`. */
/* MEDIDO NO BROWSER, E É O QUE DECIDE ESTES NÚMEROS: `.path-track` desloca-se
   uma largura de estação inteira por cada unidade de `--i`. Em `--i` 0.5 a
   estação 01 já está meia fora do ecrã pela esquerda e a 02 ocupa a outra
   metade — não existe nenhum "meio do acto" com a estação inteira no plano,
   e com o `snap` do `path.js` (que se mantém, por pedido) os estados de
   repouso são os inteiros.
   Portanto o relógio de cada acto NÃO é o troço 0..1: é a DISTÂNCIA da sua
   estação ao centro do plano. Cada acto está resolvido exactamente quando a
   sua estação assenta (o estado de repouso, o único em que se lê copy) e
   desfaz-se à medida que ela sai. Isto trabalha COM o `snap` em vez de
   contra ele, e é literalmente o que o roteiro pede na abertura do acto 02:
   "os fragmentos do acto anterior atravessam a transição" — são os mesmos
   blocos, um campo a desfazer-se enquanto o outro se organiza, no mesmo
   gesto de scroll, reversível por ser `calc()`.
   Uma segunda tentativa (relógio 0→0.34 para o acto 01) foi testada e
   rejeitada com captura: em `--i` 0.17 a coluna de texto da estação já
   estava cortada 224px pela esquerda e a linha do acto ficava ilegível
   exactamente no tempo em que devia ser lida.
   `abs()` ainda não é seguro em todos os motores; `max(a-b, b-a)` dá o
   mesmo e é suportado há muito. */
/* REVISTO A 2026-10-20, E O QUE ESTÁ ACIMA DEIXOU DE SER VERDADE. Desde o
   corte de 2026-10-12 o `path.js` arredonda `--i` (só 0/1/2/3, medido em 13
   amostras) e a pista muda por corte. As duas linhas que aqui liam `--i`
   davam por isso dois estados por acto e nenhuma transformação: o varrimento
   do acto 01 ficava parado fora do ecrã. O relógio passou a ser `--k`, escrito
   pelo `path.js` a partir do progresso NÃO arredondado: 0 no corte, onde a
   estação acabou de entrar, 1 quando assenta. A razão da rejeição de 09-25
   (estação cortada pela esquerda a meio do troço) já não existe: com o corte,
   a estação activa está sempre inteira no plano.
   O acto 01 junta-lhe `--chegada` (0..1 antes do pin), que é o arco de
   chegada que faltava: o acto monta-se enquanto a secção sobe da água. */

/* Os três tempos que o roteiro pede dentro de cada acto:
     --tA  LER          (a copy chega, o campo acorda)
     --tB  TRANSFORMAR  (o acontecimento principal)
     --tC  PROVAR       (o estado resolvido fica legível)
   São fatias sobrepostas de propósito: com fronteiras secas via-se o
   acto a mudar de andamento a meio. */
.act{
  --t: var(--k, 1);
  --tA: clamp(0, calc(var(--t) / .18), 1);
  --tB: clamp(0, calc((var(--t) - .16) / .56), 1);
  --tC: clamp(0, calc((var(--t) - .60) / .30), 1);
}
.act.act--01{ --t: min(var(--k, 1), var(--chegada, 1)); }

/* ---- o campo: o plano com profundidade --------------------------------
   Absoluto sobre a estação (que já é `position:relative`), atrás de tudo o
   resto e sem ponteiro. Sangra para fora nas quatro margens: o roteiro pede
   um plano, não um rectângulo com moldura — e uma moldura visível numa
   secção que acabou de apagar a sua própria costura seria trocar um defeito
   por outro. */
.act-field{
  /* Medidas explícitas, e não `inset` com `width:auto`: um SVG absoluto é um
     elemento substituído e o WebKit não o estica entre `left` e `right` (fica
     com o tamanho intrínseco). O campo saía deslocado para a direita no
     Safari, nos quatro actos (folha WebKit, 2026-10-20). Medido no Chromium:
     a largura esticava (116%) e a altura vinha da proporção do viewBox, não
     do `bottom`. É isso que fica escrito, para os três motores darem o mesmo
     enquadramento em que os actos foram desenhados. */
  position:absolute; left:-8%; top:-14%;
  width:116%; height:auto; aspect-ratio:1200 / 660;
  z-index:0; pointer-events:none;
  overflow:visible;
  /* nunca parte de zero: no instante em que a secção chega, os fragmentos
     JÁ estão no plano, fora de ordem — é esse o estado inicial do acto 01.
     Um campo que nasce do nada seria mais um elemento a aparecer, e o
     roteiro é explícito sobre isso no prólogo. */
  opacity: clamp(.18, calc(var(--tA) * 1.6), 1);
  /* A COLUNA DE TEXTO NÃO SE PARTILHA. Sem esta máscara as trajectórias
     passavam por cima da linha do acto e da prova — apanhado em captura, e é
     o mesmo defeito que o roteiro proíbe noutro sítio ("a água nunca passa
     por cima de texto"). O campo esvazia-se no terço esquerdo e ganha corpo
     onde há espaço: por baixo e por trás do painel da estação, que é
     translúcido de propósito. */
  -webkit-mask-image: linear-gradient(to right, transparent 0%, rgba(0,0,0,.18) 26%, #000 48%);
  mask-image: linear-gradient(to right, transparent 0%, rgba(0,0,0,.18) 26%, #000 48%);
}
.act > div,
.act > .station-live{ position:relative; z-index:1 }

/* Os três planos de profundidade do roteiro: matéria desfocada atrás,
   conteúdo ao centro, vestígios à frente. A diferença é de espessura,
   opacidade e desfocagem — não de cor, senão lê-se como três cenas. */
.af-plano{ fill:none; stroke-linecap:round }
.af-plano--longe{ stroke:var(--aqua,#17BFC1); stroke-width:.9; filter:blur(1.4px);
  opacity: calc(.30 * (.5 + var(--tB) * .5)) }
.af-plano--meio { stroke:var(--flux,#4B5CFF); stroke-width:1.5;
  opacity: calc(.56 * (.5 + var(--tB) * .5)) }
.af-plano--perto{ stroke:var(--aqua,#17BFC1); stroke-width:2.1;
  opacity: calc(.95 * (.5 + var(--tB) * .5)) }

/* As trajectórias JÁ LÁ ESTÃO quando o acto começa — é esse o estado
   inicial do roteiro ("fragmentos flutuam fora de ordem"). Desenham-se
   depressa no tempo de LER, com o desfasamento que cada uma traz inline, e
   é o VARRIMENTO que depois as acende (o `.af-plano` acima sobe de metade
   para toda a opacidade com `--tB`). Fazer o contrário — desenhá-las com o
   varrimento — punha o campo vazio no plano de entrada, que foi o que se
   viu na primeira captura desta fase.
   `pathLength="1"` normaliza o traço, por isso o `dashoffset` é
   directamente 1 - progresso. */
.af-traj{
  stroke-dasharray:1;
  stroke-dashoffset: calc(clamp(0, calc(1 - (var(--tA) - var(--dl, 0)) * 1.8), 1) * 1px);
}
/* a trajectória protagonista parte-se: este é o troço que se perde */
.af-traj--perdida{
  stroke:var(--pulse,#FF2E93);
  opacity: clamp(0, calc((var(--tB) - .34) * 5 - var(--tC) * 3), 1);
}
/* e este é o que o pulso reconstrói, no tempo de PROVAR */
.af-repair{
  fill:none; stroke:var(--volt,#C8FF2E); stroke-width:1.8; stroke-linecap:round;
  stroke-dasharray:1;
  stroke-dashoffset: calc(clamp(0, calc(1 - var(--tC)), 1) * 1px);
  /* .78 e não 1: a ligação refeita é a resolução do acto, não um néon a
     atravessar o plano — à opacidade cheia era a coisa mais brilhante do
     ecrã e roubava a leitura à copy. */
  opacity: clamp(0, calc(var(--tC) * 3), .78);
}
/* o ponto de fricção: aparece quando o varrimento lá chega e apaga-se
   quando a ligação é refeita — é o que o marca como RESOLVIDO. */
.af-quebra{
  opacity: clamp(0, calc((var(--tB) - .40) * 4 - var(--tC) * 2.4), 1);
}
.af-quebra circle{ fill:none; stroke:var(--pulse,#FF2E93); stroke-width:1.4 }
.af-quebra .af-quebra-n{ fill:var(--pulse,#FF2E93); stroke:none }
.af-quebra path{ stroke:var(--pulse,#FF2E93); stroke-width:1.6; fill:none }

/* Os rótulos são a INTENÇÃO por trás de cada fragmento, e só acendem
   quando o varrimento passa por cima deles: cada um traz o seu `--lx`, que
   é a sua posição horizontal normalizada. É isto que faz do varrimento uma
   lente e não uma barra a passear. */
.af-rotulos text{
  fill:var(--paper,#EEF1F7);
  font:500 15px/1 var(--ff-mono,ui-monospace,monospace);
  letter-spacing:.22em; text-transform:uppercase;
  opacity: clamp(0, calc((var(--tB) - var(--lx)) * 5), 1);
}

/* O varrimento: uma lâmina de luz que atravessa o plano uma vez. */
.af-varrimento{
  fill:url(#af-sweep-grad);
  transform: translateX(calc(var(--tB) * 1460px));
  /* 0.5 no pico, e não 1: à opacidade cheia a lâmina lia-se como uma barra
     branca a passear por cima do texto da estação — apanhado na primeira
     captura desta fase. É uma lente, tem de deixar ver o que ilumina. */
  opacity: clamp(0, calc(min(var(--tB) * 5, (1 - var(--tB)) * 3) * .5), .5);
  mix-blend-mode:screen;
}

/* ---- acto 02: a linha magnética e os blocos ---------------------------- */
.af-magnete{
  stroke:var(--flux,#4B5CFF); stroke-width:1.2; opacity:.55;
  transform: scaleX(var(--tB));
  transform-origin:600px 330px;
}
.af-bloco{
  fill:rgba(238,241,247,.10);
  stroke:var(--rule-strong,rgba(238,241,247,.22)); stroke-width:1;
  /* de matéria solta a peça encaixada: a MESMA conta para os sete blocos,
     cada um com o seu deslocamento e atraso inline. */
  --b: clamp(0, calc((var(--tB) - var(--dl, 0)) * 2.2), 1);
  transform:
    translate(calc(var(--fx) * 1px * (1 - var(--b))), calc(var(--fy) * 1px * (1 - var(--b))))
    rotate(calc(var(--fr) * 1deg * (1 - var(--b))));
  transform-origin:600px 330px;
  transform-box:view-box;
  opacity: calc(.35 + var(--b) * .65);
}
.af-bloco--titulo{ fill:rgba(238,241,247,.20) }
.af-bloco--prova { fill:rgba(75,92,255,.16); stroke:rgba(75,92,255,.42) }
/* O bloco do CTA marca o FIM da hierarquia, não é o objecto mais brilhante
   do plano: a .22 de preenchimento lia-se como um rectângulo verde solto por
   cima do painel da estação. Fica o contorno, que é o que diz "é aqui que
   acaba", sem competir com a copy. */
.af-bloco--cta   { fill:rgba(200,255,46,.07); stroke:rgba(200,255,46,.40) }

/* ---- acto 03: a planta dobra-se e revela a rede (2026-10-20) ----------
   Chega com a composição do acto 02 exactamente como ela ficou (as mesmas
   sete peças, na mesma posição), para o corte entre estações não trocar de
   desenho. No tempo de TRANSFORMAR a composição deita-se para trás como uma
   planta (sobe, achata e inclina) e as peças descem a seis módulos ligados
   em anel; no de PROVAR o pulso dá a volta ao anel e a cadeia do painel
   acende peça a peça. Magenta, como o roteiro pede para a construção. */
.af-planta{
  transform-box:view-box; transform-origin:600px 330px;
  transform:
    translateY(calc(var(--tB) * -150px))
    skewX(calc(var(--tB) * -30deg))
    scaleY(calc(1 - var(--tB) * .6));
  opacity: calc(.95 - var(--tB) * .6);
}
.af-peca{ fill:rgba(238,241,247,.10); stroke:var(--rule-strong,rgba(238,241,247,.22)); stroke-width:1 }
.af-peca--titulo{ fill:rgba(238,241,247,.20) }
.af-peca--prova { fill:rgba(75,92,255,.16); stroke:rgba(75,92,255,.42) }
.af-peca--cta   { fill:rgba(200,255,46,.07); stroke:rgba(200,255,46,.40) }

.af-modulo{
  fill:rgba(12,15,24,.72); stroke:rgba(255,46,147,.55); stroke-width:1.2;
  --m: clamp(0, calc((var(--tB) - var(--dl, 0)) * 2.4), 1);
  transform: translate(calc(var(--fx, 0) * 1px * (1 - var(--m))), calc(var(--fy, 0) * 1px * (1 - var(--m))));
  opacity: calc(var(--m) * .9);
}
/* o anel desenha-se depois de os módulos chegarem */
.af-anel{
  fill:none; stroke:rgba(255,46,147,.5); stroke-width:1.2;
  stroke-dasharray:1;
  stroke-dashoffset: calc((1 - clamp(0, calc((var(--tB) - .40) * 1.9), 1)) * 1px);
}
/* `stroke-dashoffset` vai sempre em `px` (1px = 1 unidade, e com
   `pathLength="1"` o caminho mede 1): o Firefox ignora um `calc()` que dá
   número sem unidade e deixava o traço desenhado e o pulso parado (visto na
   folha do Firefox, 2026-10-20).
   O pulso: um só traço curto num padrão maior do que o caminho, deslocado
   com o progresso. Só existe enquanto viaja; parado, a rede está ligada. */
.af-pulso{
  fill:none; stroke:#FFD6EA; stroke-width:3; stroke-linecap:round;
  stroke-dasharray:.035 2;
  stroke-dashoffset: calc(var(--tC) * -1.04px);
  opacity: clamp(0, min(calc(var(--tC) * 12), calc((1 - var(--tC)) * 12)), 1);
  filter: drop-shadow(0 0 6px rgba(255,46,147,.9));
}
/* a cadeia do painel é a rede legível: acende na ordem do pulso */
.act--03 .path-graph-points li{
  /* `--ordem` e não `--n`: `--n` é o número de estações que o `path.js`
     escreve no `.path`, e sem o estilo inline herdava 4 */
  --on: clamp(0, calc(var(--tC) * 6.6 - var(--ordem, 0)), 1);
  color: color-mix(in srgb, var(--paper,#EEF1F7) calc(var(--on) * 100%), var(--paper-dim,#9AA3B5));
}
.act--03 .path-graph-points li::before{
  content:""; position:absolute; inset:-1px 0; z-index:-1; pointer-events:none;
  background:rgba(255,46,147,.12); border-block:1px solid rgba(255,46,147,.7);
  opacity: var(--on);
}

/* ---- acto 04: a operação a correr (2026-10-20) ------------------------
   A rede do acto 03 chega montada mas apagada (é o mesmo desenho, para o
   corte não trocar de imagem). A rota da demo, formulário -> base de dados
   -> resposta, acende a verde e o pulso percorre-a; no tempo de PROVAR o
   resto da rede acorda. A respiração e a saída não são do relógio do acto:
   uma vem da demo (`.is-respira`), a outra do scroll depois do pin. */
/* os módulos fora da rota ficam por trás do texto e do registo da demo:
   sem preenchimento e quase apagados, para não sujarem a leitura */
.act--04 .af-modulo{ --m:1; fill:none; stroke:rgba(238,241,247, calc(.08 + var(--tC) * .12)); opacity:1 }
.act--04 .af-modulo--rota{
  --r: clamp(0, calc((var(--tB) - var(--dl, 0)) * 3), 1);
  stroke:rgba(200,255,46, calc(.18 + var(--r) * .62));
  fill:rgba(200,255,46, calc(.02 + var(--r) * .08));
}
.act--04 .af-anel{ stroke:rgba(200,255,46, calc(.08 + var(--tC) * .26)); stroke-dashoffset:0 }
.af-rota{
  fill:none; stroke:var(--volt,#C8FF2E); stroke-width:1.6; stroke-linecap:round;
  stroke-dasharray:1; stroke-dashoffset: calc((1 - var(--tB)) * 1px); opacity:.8;
}
.act--04 .af-pulso--rota{
  stroke:#F3FFD0; filter: drop-shadow(0 0 6px rgba(200,255,46,.9));
  stroke-dashoffset: calc(var(--tB) * -1.04px);
  opacity: clamp(0, min(calc(var(--tB) * 12), calc((1 - var(--tB)) * 12)), 1);
}
/* O pulso não morre: depois do pin, enquanto `#trabalho` sobe, desce do nó
   da resposta e sai pela margem inferior. */
.af-saida{
  fill:none; stroke:var(--volt,#C8FF2E); stroke-width:1.4; stroke-linecap:round;
  stroke-dasharray:1; stroke-dashoffset: calc((1 - var(--saida, 0)) * 1px); opacity:.7;
}
.act--04 .af-pulso--saida{
  stroke:#F3FFD0; filter: drop-shadow(0 0 6px rgba(200,255,46,.9));
  stroke-dashoffset: calc(var(--saida, 0) * -1.04px);
  opacity: clamp(0, min(calc(var(--saida, 0) * 12), calc((1 - var(--saida, 0)) * 12)), 1);
}
/* no fim de cada corrida da demo, a rede inteira respira uma vez */
@keyframes af-respira{
  0%,100%{ filter:none; transform:none }
  35%{ filter:brightness(1.7) drop-shadow(0 0 10px rgba(200,255,46,.35)); transform:scale(1.012) }
}
.station.is-respira .act-field--04{ animation: af-respira 1.4s cubic-bezier(.22,.61,.36,1) 1 }

/* ---- a copy dos actos -------------------------------------------------
   A linha do acto SUBSTITUI o `<h3>` no topo da hierarquia da estação; o
   `<h3>` desce a rótulo (ver o ramo do percurso fixado). O `<h2>` da secção
   NÃO é tocado: a contagem de títulos por ecrã fica igual à que já existia
   (secção + estação), não sobe. Uma tentativa de o apagar durante os actos
   foi desfeita — deixava a secção sem título no seu próprio estado de
   repouso, que é pior do que o problema que resolvia.

   `.path--run .station p` (path.css) tem mais especificidade do que uma
   classe só, e ganhava a esta regra: a linha do acto saía a 15px por cima
   da prova, tudo sobreposto. Daí o selector completo. */
.path--run .station .act-line,
.station .act-line,
.act-line{
  margin:0 0 14px;
  font-family:var(--ff-display); font-weight:700;
  font-size:clamp(19px,min(2.1vw,2.9vh),29px);
  line-height:1.1; letter-spacing:-.025em; color:var(--paper);
  max-width:22ch;
  opacity: var(--tA);
  transform: translateY(calc((1 - var(--tA)) * 14px));
}
/* O título do acto 02 só se torna legível quando as peças se alinham, e
   volta a desfazer-se ao recuar — é o momento memorável que o roteiro pede,
   feito com desfocagem em vez de partir a frase em spans (partir o texto
   em elementos separados quebrava o `data-pt/-en/-es/-pt-br`, que é como
   as quatro línguas desta página são geradas). */
.act--02 .act-line{
  filter: blur(calc((1 - var(--tC)) * 5px));
  letter-spacing: calc(-.025em + (1 - var(--tC)) * .14em);
}
.path--run .station .act-proof,
.station .act-proof,
.act-proof{
  margin:14px 0 0;
  font:500 10px/1.6 var(--ff-mono,ui-monospace,monospace);
  letter-spacing:.11em; text-transform:uppercase;
  color:var(--aqua,#17BFC1);
  max-width:50ch;
  opacity: clamp(0, calc(var(--tC) * 1.4), 1);
}

/* ---- prólogo ----------------------------------------------------------
   Entra com a régua e sai antes do acto 01 tomar o plano. Vive fora dos
   actos, por isso lê `--i` directamente. Fora do percurso fixado fica
   simplesmente presente, como qualquer outra linha de entrada. */
.act-prologo{
  margin:0 0 clamp(14px,2vh,26px);
  font:500 12px/1.5 var(--ff-mono,ui-monospace,monospace);
  letter-spacing:.14em; text-transform:uppercase;
  color:var(--aqua,#17BFC1);
  max-width:52ch;
}

/* ======================================================================
   Só no percurso fixado (>=900px, sem movimento reduzido): é aqui que a
   cena se comporta como cena. Fora daqui fica tudo no estado resolvido.
   ====================================================================== */
@media(min-width:900px){
  .path--run .act-prologo{
    /* presente no instante em que a secção chega (`--i` = 0) e fora antes de
       o acto 01 tomar o plano. Sem rampa de entrada de propósito: a entrada
       é a chegada da água, não um segundo aparecimento. */
    opacity: clamp(0, calc((.20 - var(--i, 0)) * 9), 1);
  }
  /* o `<h3>` da estação passa a rótulo enquanto o acto tem a linha
     dominante. Desde 2026-10-20 nas quatro estações (03 e 04 têm cena). */
  .path--run .act h3{
    font-size:clamp(12px,1.5vh,13.5px);
    font-family:var(--ff-mono,ui-monospace,monospace);
    font-weight:500; letter-spacing:.16em; text-transform:uppercase;
    color:var(--paper-faint); margin-bottom:10px;
  }
  /* a estação fora de foco já vai a `opacity:.34` (path.css); o campo
     desaparece de todo, senão via-se o acto seguinte a desenhar-se ao lado
     do que está a ser lido. */
  .path--run .station:not(.is-live) .act-field{ opacity:0 }
}

/* ======================================================================
   Coluna (<900px) e movimento reduzido: composições estáticas completas.
   `--i` não existe nestes modos, por isso os valores de recurso acima já
   entregam `--t = 1` — o acto resolvido. Aqui só se corrige o que é
   layout, e desliga-se o que só faz sentido em movimento.
   ====================================================================== */
@media(max-width:899px){
  /* EM COLUNA O CAMPO NÃO PODE FICAR ATRÁS DO TEXTO. Em paisagem a máscara
     horizontal chegava — a copy vive no terço esquerdo e o campo no resto.
     A 390px a copy ocupa a largura toda, e o campo passava por cima das
     letras (capturado: os rótulos do mapa em cima do `<h3>`). Aqui deixa de
     ser camada e passa a ser BANDA: entra no fluxo, entre a copy e o painel
     da estação, com a composição já resolvida. É a leitura que o roteiro
     pede em retrato — vertical, com a transformação central mantida. */
  .act-field{
    position:static; order:2;
    inset:auto; width:100%; height:clamp(150px,24svh,230px);
    opacity:.9; margin:4px 0 2px;
    -webkit-mask-image:none; mask-image:none;
  }
  .act > div:not(.station-live){ order:1 }
  .act > .station-live{ order:3 }
  /* e o `<h3>` desce a rótulo também aqui, senão ficam duas linhas com peso
     de título uma debaixo da outra. */
  .act h3{
    font-size:12.5px; font-family:var(--ff-mono,ui-monospace,monospace);
    font-weight:500; letter-spacing:.16em; text-transform:uppercase;
    color:var(--paper-faint); margin-bottom:10px;
  }
  /* a rede dos actos 03/04 está desenhada à volta do painel do desktop, à
     direita; na banda ficava encostada à margem. Centra-se aqui. */
  .act-field .af-anel, .act-field .af-modulos, .act-field .af-pulso,
  .act-field .af-rota, .act-field .af-saida{ transform:translateX(-300px) }
  .station .act-line{ font-size:clamp(20px,5.4vw,26px); max-width:none }
  .station .act-proof{ font-size:9.5px; letter-spacing:.09em }
}
@media(prefers-reduced-motion:reduce){
  /* sem varrimento: uma lâmina de luz parada no meio do plano lê-se como
     uma faixa, não como um acontecimento. O resto da composição fica. */
  .af-varrimento, .af-pulso{ display:none }
  .station.is-respira .act-field--04{ animation:none }
  /* em desktop com movimento reduzido não há `.path--run` nem coluna: sem
     isto o `<h3>` ficava com peso de título por baixo da linha do acto,
     duas linhas fortes na mesma estação (visto em captura, 2026-10-20) */
  .act h3{
    font-size:12.5px; font-family:var(--ff-mono,ui-monospace,monospace);
    font-weight:500; letter-spacing:.16em; text-transform:uppercase;
    color:var(--paper-faint); margin-bottom:10px;
  }
  .act-field{ opacity:.8 }
  .act-line, .act-proof, .act-prologo{ opacity:1!important; transform:none!important; filter:none!important }
  .act--02 .act-line{ letter-spacing:-.025em }
}
/* Desktop com movimento reduzido: não há pin e o painel ficava com a altura
   da coluna (32vh). Os campos foram desenhados à volta do painel do percurso
   fixado (42vh), e a rede do acto 03 saía por baixo dele (visto em captura,
   2026-10-20). Mesma altura, mesmo desenho. */
@media(prefers-reduced-motion:reduce) and (min-width:900px){
  .act > .station-live{ min-height:clamp(240px,42vh,400px) }
}
