/* ============================================================================
   CENAS — o que cada cena tem de próprio, além dos componentes de base.css.
   Fonte de verdade: 01-roteiro-cena-a-cena.md PARTE 2 (storyboard S01–S31)
                     e PARTE 3 (animações-chave).

   Convenções deste arquivo:
   · Um bloco por cena, na ordem do deck, com o título e a duração do roteiro.
   · Seletor de cena por id (#S01…#S31); dentro dele, classes semânticas.
   · Estado de batida por [data-beat="n"] — o CSS reage sem JS sempre que dá,
     porque o que o CSS resolve sozinho não pode quebrar no palco.
   · Nada de tamanho em px: o palco define 1rem = 16px @1080 (stage.css).
   ========================================================================== */

/* --- Os quatro tamanhos que o roteiro exige literalmente e que a escala
   tipográfica de §4.3 não tem. Ficam escopados no palco, e não em :root, para
   deixar explícito que são exceções de cena e não tokens do sistema. Se
   qualquer um deles for reutilizado numa terceira cena, promova a tokens.css. */
.stage {
  --fs-formula: 7rem;   /* 112px — fórmula do attention (S10, S13)           */
  --fs-timer:  5.5rem;  /* 88px  — cronômetro da demo e numerais da S26      */
  --fs-kpi:      4rem;  /* 64px  — os 4 indicadores da S18                   */
  --fs-72m:    4.5rem;  /* 72px  — blocos de dado em mono (S11, S14)         */
}

/* ============================================================================
   ATO 0 — ABERTURA PESSOAL
   ========================================================================== */

/* --- S01 · Capa · 0:20 --------------------------------------------------- */
#S01 {
  background: radial-gradient(120% 90% at 50% 115%,
              var(--primary) 0%, var(--bg-800) 45%, var(--bg-900) 100%);
}
/* Textura de partículas do site: duas grades de pontos, 48px e 64px. */
#S01::before {
  content: ''; position: absolute; inset: 0; pointer-events: none; opacity: .06;
  background-image:
    radial-gradient(rgba(255,255,255,.9) 1px, transparent 1px),
    radial-gradient(rgba(255,255,255,.6) 1px, transparent 1px);
  background-size: 3rem 3rem, 4rem 4rem;
  background-position: 0 0, 1.5rem 1.5rem;
}

/* Anéis outline gigantes, sangrando pelas bordas. */
.ring {
  position: absolute; border-radius: var(--r-full);
  border: 1px solid rgba(255,255,255,.07); pointer-events: none;
}
.ring--a { width: 28rem; height: 28rem; top: -7rem;    right: -6rem }
.ring--b { width: 34rem; height: 34rem; bottom: -11rem; left: -9rem }

/* A linha de ECG do simpósio. Desenha-se uma vez em 2,4 s e depois só o
   complexo QRS pulsa — por isso o QRS é um path separado: aplicar o
   drop-shadow na linha inteira acenderia o traçado todo. */
.ecg {
  position: absolute; left: 0; right: 0; top: 50%; height: 7.5rem;
  transform: translateY(-50%); opacity: .30; pointer-events: none; z-index: 0;
}
.ecg svg { display: block; width: 100%; height: 100% }
.ecg path {
  fill: none; stroke: var(--accent); stroke-width: 3;
  stroke-linecap: round; stroke-linejoin: round;
}
.ecg__line {
  stroke-dasharray: var(--len, 1600); stroke-dashoffset: var(--len, 1600);
  animation: strokeDraw 2.4s var(--ease-out-expo) forwards;
}
.ecg__qrs { animation: ecgBeat 4s 2.6s infinite }
@keyframes strokeDraw { to { stroke-dashoffset: 0 } }
@keyframes ecgBeat {
  0%, 86%, 100% { filter: none }
  92%           { filter: drop-shadow(0 0 1.25rem var(--accent)) }
}
/* Na S30 a mesma linha corre continuamente, sem o desenho inicial. */
.ecg--loop .ecg__line {
  stroke-dashoffset: 0; stroke-dasharray: 2rem 1.25rem;
  animation: ecgRun 6s linear infinite;
}
@keyframes ecgRun { to { stroke-dashoffset: -20rem } }

.capa { position: relative; z-index: 1; display: grid; align-content: center; gap: var(--sp-3); height: 100% }
.capa__title { max-width: 66rem }
.capa__sub   { color: var(--primary-soft) }
.capa__name  { margin-top: var(--sp-3) }
.capa__foot {
  position: absolute; left: var(--margin-h); bottom: var(--margin-v);
  font: 400 var(--fs-ref)/1.3 var(--font-mono); color: var(--txt-low);
}
.capa__brand { position: absolute; right: var(--margin-h); bottom: var(--margin-v) }
/* Saída: título e subtítulo sobem 40px e se dissolvem enquanto o ECG contrai
   para o centro — o mesmo traço que reaparece como divisor do card de bio da
   S02. Antes isso era uma BATIDA (clique 1 desmontava a capa, clique 2 trocava
   de cena) e do palco lia-se como um clique perdido; desde 11/08 acontece na
   SAÍDA, num clique só. A contração é transform puro, sem FLIP: cabe nos
   340 ms que o motor dá à cena que sai e não depende de medir geometria. */
#S01 .capa__title, #S01 .capa__sub, #S01 .ecg {
  transition: opacity var(--dur-fast) var(--ease-in-out),
              transform var(--dur-fast) var(--ease-in-out);
}
#S01[data-state="leaving"] .capa__title,
#S01[data-state="leaving"] .capa__sub { opacity: 0; transform: translateY(-2.5rem) }
#S01[data-state="leaving"] .ecg { transform: scaleX(.125) }

/* --- S02 · Quem sou · 0:35 ---------------------------------------------- */
#S02 .bio {
  display: grid; grid-template-columns: 5fr 7fr; gap: var(--sp-6);
  align-items: center; height: 100%;
}
.bio__portrait {
  position: relative; width: 30rem; height: 37.5rem;
  border-radius: var(--r-xl); border-bottom: 3px solid var(--accent);
  overflow: hidden; background: var(--bg-800);
}
/* O monograma FC é o caminho PADRÃO da cena, não um plano B (pendência 3 da
   PARTE 7: a foto oficial em alta só existe a partir de 07/08). Não há <img>
   nenhum na S02 — logo, não há como o telão exibir ícone de imagem quebrada.
   A foto entra como camada de background por cima do monograma, e só quando
   data-photo="ok": enquanto o atributo for "none" o arquivo sequer é pedido,
   então não há 404 no console. E se for pedido e faltar, o background
   simplesmente não pinta — o monograma continua à vista. */
.bio__mono {
  display: grid; place-items: center; height: 100%;
  border: 3px solid var(--primary); border-radius: var(--r-xl);
  font: 800 var(--fs-dxl)/1 var(--font-display);
  letter-spacing: var(--tr-dxl); color: var(--txt-high);
}
.bio__portrait::before {
  content: ''; position: absolute; inset: 0; z-index: 1;
  background-position: center; background-size: cover; background-repeat: no-repeat;
}
.bio__portrait[data-photo="ok"]::before {
  background-image: url('../assets/img/retrato-fernando.jpg');
}
.bio__portrait::after {
  content: ''; position: absolute; inset: 0; z-index: 2;
  background: linear-gradient(180deg, transparent 40%, rgba(5,11,24,.85));
}
.bio__cards { display: grid; gap: var(--sp-3) }
.bio__card {
  display: grid; grid-template-columns: 2.5rem 1fr; gap: var(--sp-3);
  align-items: start;
  transition: transform var(--dur-fast) var(--ease-in-out),
              opacity var(--dur-fast), filter var(--dur-fast);
}
.bio__card svg { width: 2.5rem; height: 2.5rem; color: var(--primary-soft) }
/* Saída (era batida, virou saída em 11/08 — ver a nota da S01). O blur fica
   só aqui: é um filtro em três elementos durante 340 ms, não estado de cena. */
#S02[data-state="leaving"] .bio__card { transform: scale(.96); opacity: .25; filter: blur(3px) }

/* --- S03 · Conflito de interesse · 0:20 · data-cut="3" ------------------- */
/* A ÚNICA cena de cor cheia vermelha da palestra. É a escassez que a torna
   memorável — não repetir em nenhuma outra cena (§4.2, contrato do --accent). */
#S03 { color: var(--txt-high) }
#S03 .drain {
  position: absolute; inset: 0; z-index: 0; background: var(--accent);
  clip-path: inset(0);
  transition: clip-path var(--dur-base) var(--ease-in-out);
}
#S03 .s03__body { position: relative; z-index: 1 }
/* O vermelho drena de baixo para cima e entrega a S04 que está por trás. */
#S03[data-beat="1"] .drain,
#S03[data-state="leaving"] .drain { clip-path: inset(0 0 100% 0) }
#S03 .list-clean .tick { color: var(--txt-high) }
#S03 .s03__foot { opacity: .7 }   /* branco a 70%, sem inventar cor nova */

/* ============================================================================
   ATO 0b — OS DOIS QR CODES
   ========================================================================== */

/* --- S04 · Duas portas · 1:00 · NUNCA CORTAR ---------------------------- */
#S04 .qr-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-7);
  align-content: center; height: 100%;
}
/* Branco puro é obrigatório aqui e só aqui: QR nunca sobre fundo escuro nem
   sobre foto. A tinta do cartão também não pode vir de --txt-*, porque no
   tema claro de emergência os tokens de texto invertem e o cartão não. */
.card-qr {
  --qr-ink: #0A1E3D; --qr-ink-soft: #5B6B85;
  position: relative;
  background: #FFFFFF; border-radius: var(--r-xl); padding: var(--sp-5);
  box-shadow: 0 0 0 1px rgba(255,255,255,.12), 0 40px 80px -20px rgba(11,77,166,.5);
  color: var(--qr-ink);
  display: grid; gap: var(--sp-3); justify-items: center;
  transition: transform var(--dur-base) var(--ease-in-out), opacity var(--dur-base);
}
.card-qr__tab {
  position: absolute; top: calc(var(--sp-4) * -1); left: var(--sp-5);
  padding: var(--sp-1) var(--sp-3); border-radius: var(--r-sm) var(--r-sm) 0 0;
  font: 700 var(--fs-label)/1 var(--font-mono); color: var(--txt-high);
}
.card-qr--wa   .card-qr__tab { background: var(--primary) }
.card-qr--deck .card-qr__tab { background: var(--accent) }
.card-qr__code { display: block; width: 26.25rem; height: 26.25rem }
/* O selo central branco não é mais um elemento sobreposto: ele nasce dentro do
   SVG gerado (07-scripts/gerar-qr.mjs), com 9 módulos de diâmetro. É o que faz
   ele encolher junto com o cartão da S31 em vez de ficar desproporcional — e é
   o que permite ao script provar, decodificando o próprio desenho, que o selo
   não come mais módulos do que o nível H aguenta (9 de 33 = 5,8% da área,
   contra os 30% de margem). A quiet zone de 4 módulos também é do SVG. */
.card-qr__figure { position: relative }
/* margin:0 nos três: o espaçamento do cartão é o gap do grid, e a margem de 1em
   do agente de usuário somava 244px de vazio — o bastante para empurrar as
   duas URLs para fora do pé do palco, cortadas ao meio. */
.card-qr__title { font: 700 var(--fs-h2)/var(--lh-h2) var(--font-display); color: var(--qr-ink); margin: 0 }
.card-qr__desc  { font: 500 var(--fs-body)/var(--lh-body) var(--font-body); color: var(--qr-ink-soft); text-align: center; margin: 0 }
.card-qr__url   { font: 400 var(--fs-ref)/1.3 var(--font-mono); color: var(--qr-ink); margin: 0 }
/* Os dois cards recuam e viajam para as âncoras do topo, onde o chrome
   (.qr-mini, stage.css) assume. Voltam a tamanho cheio na S31.
   Era batida — e era a pior das quatro, porque deixava a tela vazia esperando
   um segundo clique. Virou saída em 11/08. */
#S04 .card-qr {
  transition: transform var(--dur-fast) var(--ease-in-out), opacity var(--dur-fast);
}
#S04[data-state="leaving"] .card-qr { transform: scale(.72) translateY(-28%); opacity: 0 }
#S04 .s04__foot { color: var(--warn) }

/* ============================================================================
   ATO 0c — DINÂMICA E O RECADO ANTECIPADO
   ========================================================================== */

/* --- S05 · O mapa da fala · 0:25 ---------------------------------------- */
#S05 .track {
  position: relative; height: 11.25rem;
  display: flex; justify-content: space-between; align-items: flex-start;
  transition: opacity var(--dur-base), transform var(--dur-base) var(--ease-in-out);
}
.track::before {
  content: ''; position: absolute; left: 0; right: 0; top: 2rem;
  height: 2px; background: rgba(255,255,255,.15);
}
.track__item { position: relative; display: grid; justify-items: center; gap: var(--sp-2); width: 13.75rem }
.track__node {
  position: relative; width: 4rem; height: 4rem; border-radius: var(--r-full);
  border: 2px solid var(--stroke-hi); background: var(--bg-900);
  display: grid; place-items: center;
  font: 700 var(--fs-body)/1 var(--font-mono); color: var(--txt-mid);
}
.track__label { font: 500 var(--fs-body)/1.2 var(--font-body); color: var(--txt-low); text-align: center }
.track__item[data-live="1"] .track__node {
  background: var(--accent); border-color: var(--accent);
  color: var(--txt-high); transform: scale(1.25);
}
.track__item[data-live="1"] .track__label { color: var(--txt-high) }
/* Halo por escala e opacidade, não por box-shadow animado: só transform,
   opacity, filter e clip-path entram em animação neste deck. */
.track__item[data-live="1"] .track__node::after {
  content: ''; position: absolute; inset: -.5rem; border-radius: var(--r-full);
  border: 2px solid var(--accent); animation: halo 2s ease-out infinite;
}
@keyframes halo {
  from { transform: scale(.9);  opacity: .75 }
  to   { transform: scale(1.7); opacity: 0 }
}
/* O trilho colapsa para a barra de 4px do rodapé. Saída, não batida (11/08):
   o clique que colapsava o trilho não trocava de cena, e o palestrante ficava
   olhando para um mapa esmagado esperando que alguma coisa acontecesse. */
#S05[data-state="leaving"] .track { opacity: 0; transform: translateY(24rem) scaleY(.04) }

/* --- S05B · A mesa dos artigos · 0:40 · 1 batida ------------------------
   Oitenta e nove cartões em movimento simultâneo. Três regras governam esta
   cena inteira e existem por causa desse número:

   1. Só `transform` e `opacity` se movem. Nada de box-shadow, filter ou
      largura animada — cada uma delas força layout ou pintura em 89 caixas.
   2. A posição de cada card mora em custom properties inline, calculadas uma
      única vez pelo script da cena. O CSS só escolhe QUAL par usar, conforme
      a fase; trocar `data-fase` reescreve 89 transforms num só reflow.
   3. `will-change` só existe durante a chegada. Ligado o tempo todo, 89
      camadas de composição fazem um notebook comum derrubar o frame rate no
      meio do Ato 0 — que é justamente onde a plateia decide se confia na
      apresentação.

   Tipografia: os cards são `.t-ref` de verdade — cada um É uma referência
   bibliográfica —, e por isso vivem no piso de 26px que §5.2 abre para essa
   classe. Espalhados e em escala .71 eles não são texto de leitura, e não
   deveriam ser: a leitura acontece no celular de quem abriu o deck pelo QR e
   no card que cresce sob o cursor. Quem lê a tela lê o rótulo do grupo. */
#S05B { overflow: hidden }
.mesa__eyebrow {
  position: absolute; left: var(--margin-h); top: var(--margin-v);
  margin: 0; color: var(--txt-low);
}
.mesa {
  position: absolute;
  left: var(--margin-h); right: var(--margin-h);
  top: 7rem; bottom: 5rem;
}
.mesa__pilha {
  position: relative; width: 100%; height: 100%;
  /* De onde os cards partem: o centro da mesa, em escala .2. É a mesma origem
     para todos, e é o que faz a chegada parecer uma busca que trouxe tudo de
     uma vez em vez de uma lista sendo digitada. */
  --cx: 52.25rem; --cy: 26.6rem;
}
.mesa__fallback { position: absolute; inset: 0; display: grid; place-content: center; text-align: center }
.mesa__pilha[data-pronto="1"] .mesa__fallback { display: none }

.card-art {
  position: absolute; left: 0; top: 0;
  width: 17.5rem; height: 9.25rem;
  transform-origin: 0 0;                    /* o canto manda; o cálculo fica trivial */
  z-index: var(--z, 0);
  display: flex; flex-direction: column; justify-content: space-between;
  padding: .75rem .875rem;
  border: 1px solid var(--stroke);
  border-left: 3px solid transparent;
  border-radius: var(--r-sm);               /* papel tem canto duro */
  background: var(--bg-700);
  text-decoration: none; overflow: hidden;
  transition: transform var(--dur-slow) var(--ease-out-expo),
              border-color var(--dur-fast), background var(--dur-fast);
  transition-delay: calc(var(--i, 0) * 3ms);
}
/* Quem tem PDF nosso ganha o filete azul. Não é decoração: é a diferença
   entre "abre o artigo" e "abre a página do artigo", e quem tocar percebe. */
.card-art[data-destino="pdf"] { border-left-color: var(--primary-soft) }
/* Card mudo: três normas de 2013 cujo endereço oficial não abre em https
   (Biblioteca Virtual em Saúde). Sem link é melhor que com link quebrado —
   `07-scripts/gerar-artigos.mjs --verificar` bate as 86 URLs antes de cada
   publicação, e o que não abre não vira porta. */
.card-art:not([href]) { opacity: .62; cursor: default }
.card-art__t {
  font: 600 var(--fs-ref)/1.22 var(--font-body); color: var(--txt-high);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3;
  overflow: hidden;
}
.card-art__a { font: 400 var(--fs-ref)/1.2 var(--font-body); color: var(--txt-low) }

/* ---- fase 1: em cima da mesa ---- */
.mesa[data-fase="mesa"] .card-art {
  transform: translate3d(var(--mx), var(--my), 0) rotate(var(--mr)) scale(.71);
}
/* ---- fase 2: cinco pilhas ---- */
.mesa[data-fase="grupos"] .card-art {
  transform: translate3d(var(--gx), var(--gy), 0) rotate(0deg) scale(.36);
}
/* Voltar da S06 não pode reencenar 900 ms de reorganização (CONTRATO §2). */
.mesa[data-instantaneo="1"] .card-art { transition: none }

/* A chegada. `both` segura o estado final até o script tirar data-entrada;
   a partir daí o transform volta a ser o da regra de fase, que é idêntico. */
.mesa[data-entrada="1"] .card-art {
  animation: cardChega var(--dur-slow) var(--ease-out-expo) both;
  animation-delay: calc(var(--i) * 12ms);
  will-change: transform;
}
@keyframes cardChega {
  from { opacity: 0; transform: translate3d(var(--cx), var(--cy), 0) rotate(0deg) scale(.2) }
  to   { opacity: 1; transform: translate3d(var(--mx), var(--my), 0) rotate(var(--mr)) scale(.71) }
}

/* O card sob o cursor volta ao tamanho de leitura. Só vale na projeção; é a
   affordance de "isto aqui é clicável" para quem está com o deck na mão. */
.mesa[data-fase="mesa"] .card-art:hover {
  transform: translate3d(var(--mx), var(--my), 0) rotate(0deg) scale(1.04);
}
.mesa[data-fase="grupos"] .card-art:hover {
  transform: translate3d(var(--gx), var(--gy), 0) scale(.92);
}
.card-art:hover { z-index: 200; border-color: var(--primary-soft); background: var(--bg-600) }

/* ---- os rótulos das cinco pilhas ---- */
.mesa__rotulos { position: absolute; inset: 0; pointer-events: none }
.mesa__rotulo {
  position: absolute; top: 0; width: 20%;
  left: calc(var(--col) * 20%);
  display: grid; justify-items: start; gap: .25rem;
  opacity: 0; transform: translateY(-1rem);
  transition: opacity var(--dur-base) var(--ease-out-expo),
              transform var(--dur-base) var(--ease-out-expo);
  transition-delay: 320ms;                  /* entram depois que a massa assenta */
}
.mesa[data-fase="grupos"] .mesa__rotulo { opacity: 1; transform: none }
.mesa__rotulo b {
  font: 700 var(--fs-label)/1 var(--font-display);
  letter-spacing: var(--tr-label, .06em); color: var(--txt-high);
}
.mesa__rotulo i {
  font: 700 var(--fs-h1)/1 var(--font-display); font-style: normal;
  color: var(--primary-soft);
}
.mesa__pe { position: absolute; left: var(--margin-h); right: var(--margin-h); bottom: 1.5rem }

/* Movimento reduzido: a mesa continua sendo mesa, mas ninguém voa. */
:root[data-motion="reduced"] .card-art,
:root[data-motion="reduced"] .mesa__rotulo { transition: none; animation: none }

/* Divisor de grupo do modo de leitura. Injetado pelo script da cena junto com
   os cards; na projeção não existe visualmente — a mesa não tem divisores. */
.mesa__divisor { display: none }

/* ═══ A FAIXA DE PROVAS ═══════════════════════════════════════════════════
   A citação de rodapé virada em card clicável (12/08/2026). Ver js/provas.js
   para o contrato de declaração e o porquê do fallback.

   Por que não reaproveita .card-art: aquele é `position: absolute` e vive de
   --mx/--my/--gx/--gy, que são as coordenadas da mesa da S05B. Aqui os cards
   estão em FLUXO, dividindo a largura em partes iguais. A tipografia é a
   mesma, a geometria não podia ser.

   A hierarquia é invertida em relação à mesa, e de propósito: no telão, a
   sete metros, o que se lê é "Wong et al., JAMA Intern Med 2021" — o título
   do artigo é para quem está com o deck na mão. Por isso a autoria vem em
   cima, em 600, e o título embaixo, em --txt-low. */
/* 9rem, e não as 7rem do primeiro desenho: com SEIS cards a coluna cai para
   250px e "Krizhevsky et al., NIPS 2012" não cabe numa linha só. Ver o
   comentário de .prova__a — cortar a autoria com reticências seria cortar
   exatamente a parte que se lê de longe. */
/* A altura da faixa é uma variável da CENA, não da faixa: a nota metodológica
   é IRMÃ da faixa (não filha — ela precisa ficar por cima, e a faixa é um
   flex de cards), então ela precisa saber onde a faixa termina para se
   ancorar. Uma cena que aperta a faixa (ver #S18) muda os dois de uma vez. */
.scene { --faixa-h: 9rem; --faixa-b: var(--sp-3) }

.provas {
  position: absolute; left: var(--margin-h); right: var(--margin-h);
  bottom: var(--faixa-b); height: var(--faixa-h);
  /* gap de 8px e não 16: com seis cards, os 40px que sobram são a diferença
     entre "Weizenbaum, Commun ACM 1966" inteiro e "Commun ACM…". */
  display: flex; gap: var(--sp-1); align-items: stretch; z-index: 6;
  /* Centralizado por causa das cenas de UMA referência só (S13, S19, S28,
     S26): com `flex: 1` puro, um card sozinho esticaria por 1728px e a
     citação viraria um cartaz. O max-width do .prova segura, e o center
     resolve o que sobra. Com cinco ou seis cards nada disso tem efeito. */
  justify-content: center;
}
/* Enquanto os cards não existirem, o que está em tela é a citação escrita —
   e ela é o mesmo texto que sempre esteve ali. */
.provas[data-pronto="1"] > .scene__ref { display: none }
.provas > .scene__ref { position: static; left: auto; right: auto; bottom: auto }

.prova {
  flex: 1 1 0; min-width: 0; max-width: 26rem;
  display: flex; flex-direction: column; gap: .125rem;
  padding: var(--sp-1) .75rem;
  border: 1px solid var(--stroke);
  border-left: 3px solid transparent;
  border-radius: var(--r-sm);
  background: var(--bg-700);
  text-decoration: none; overflow: hidden;
  transition: border-color var(--dur-fast), background var(--dur-fast),
              transform var(--dur-fast) var(--ease-out-expo);
}
/* Mesmo código de cor da mesa: filete azul = PDF nosso, hospedado. */
.prova[data-destino="pdf"] { border-left-color: var(--primary-soft) }
.prova:not([href]) { opacity: .62 }
/* A autoria QUEBRA, não corta. Com `nowrap` + ellipsis a faixa da S11 virava
   "Wong et al., JAM…", "Shashikumar et a…", "Bhargava et al., N…" — e a
   autoria é justamente o que a plateia lê a sete metros; o título é para
   quem está com o deck na mão. Duas linhas para cada um. */
.prova__a {
  font: 600 var(--fs-ref)/1.15 var(--font-body); color: var(--txt-high);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden;
}
.prova__t {
  font: 400 var(--fs-ref)/1.15 var(--font-body); color: var(--txt-low);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden;
}
a.prova:hover {
  border-color: var(--primary-soft); background: var(--bg-600);
  transform: translateY(-.25rem);
}

/* Faixa presa a uma batida: a S11 tem duas, e elas trocam com a ênfase da
   cena. Só opacidade e pointer-events — trocar o conteúdo por JS faria o
   navegador remontar seis cards no meio de uma transição.

   O estado BASE é oculto, e a regra condicional é a que acende. Escrito ao
   contrário — base visível, condicional apagando — a faixa da batida errada
   nascia em opacity 1 e levava os 600 ms da transição para sumir: meio
   segundo, na entrada da cena, com duas faixas de citação empilhadas no
   mesmo lugar. Foi o `verifica-rodape.mjs` que viu, e ele estava certo. */
.provas[data-em-beat] { opacity: 0; pointer-events: none; transition: opacity var(--dur-base) }
.scene[data-beat="0"] .provas[data-em-beat="0"],
.scene[data-beat="1"] .provas[data-em-beat="1"] { opacity: 1; pointer-events: auto }

/* A NOTA METODOLÓGICA, logo acima da faixa. Existe porque em cinco cenas
   (S18, S19, S19B, S28, S29) o rodapé nunca foi só citação: ele carrega a ressalva
   que sustenta o número contra a pergunta difícil — "dados ilustrativos",
   "a soma dos tempos de disfunção e ATB", as simulações de Monte Carlo, os
   aOR de dano do SCREEN. Isso não é fonte, é defesa: não pode virar tooltip
   dentro de um card nem migrar para as notas do palestrante. Fica em tela,
   por cima da faixa, e a cena reserva --provas-band-nota em vez de
   --provas-band. */
.provas__nota {
  position: absolute; left: var(--margin-h); right: var(--margin-h);
  bottom: calc(var(--faixa-h) + var(--faixa-b) + var(--sp-1));
  color: var(--txt-low); z-index: 6;
}

/* FAIXA COMPACTA — só a autoria, sem o título do artigo.
   Existe por causa da S18, que é um painel operacional com quatro faixas de
   conteúdo somando ~816px: dar a ela os 176px da faixa cheia empurraria o
   protocolo para fora da tela. Aqui o card continua sendo a prova de fonte e
   continua abrindo o artigo — só não mostra o título, que naquela cena
   ninguém ia ler mesmo. */
#S18 { --faixa-h: 3.75rem }
#S18 .prova__t { display: none }
/* Duas linhas de autoria a 24px, e não uma a 26: "ILAS, Roteiro de
   Implementação, 5ª ed." tem 37 caracteres e saía como "ILAS, Roteiro de…" —
   um card de citação que corta o nome do documento não é citação. */
#S18 .prova { padding: .25rem .75rem; justify-content: center }
#S18 .prova__a { font-size: var(--fs-label); line-height: 1.05 }

:root[data-motion="reduced"] .prova,
:root[data-motion="reduced"] .provas { transition: none }

/* --- S06 · O recado, antecipado · 0:35 · NUNCA CORTAR ------------------- */
#S06 {
  background:
    radial-gradient(60% 50% at 50% 45%, rgba(11,77,166,.22), transparent),
    var(--bg-900);
}
.words { display: flex; flex-wrap: wrap; gap: var(--sp-3) var(--sp-5); justify-content: center }
.words__w {
  font: 800 var(--fs-d2)/var(--lh-d2) var(--font-display);
  letter-spacing: var(--tr-d2); color: var(--primary-soft);
  opacity: 0; transform: translateY(1.5rem);
  animation: wordIn .42s var(--ease-out-expo) calc(var(--i, 0) * 180ms) both;
}
.words__w[data-emph="accent"] { color: var(--accent) }
@keyframes wordIn { to { opacity: 1; transform: none } }

.s06__quote { max-width: 72rem }
.s06__punch { color: var(--accent) }
#S06 .s06__quote, #S06 .s06__punch, #S06 .rule-thin {
  transition: opacity 400ms var(--ease-in-out);
}
/* Tudo sai, menos HUMANIZADA — que viaja para o canto e vira marca d'água
   até a S30 (.watermark-hz, stage.css). A régua sai junto: ela divide um
   texto que deixou de existir, e sozinha na tela vira um risco solto. */
#S06[data-beat="1"] .s06__quote,
#S06[data-beat="1"] .s06__punch,
#S06[data-beat="1"] .rule-thin { opacity: 0 }

/* As três palavras saem por ANIMAÇÃO, e não por `opacity: 0`: `wordIn` roda
   com fill-mode `both`, e valor preenchido por animação vence declaração na
   cascata — só outra animação o substitui. Os keyframes trazem `from`
   explícito porque o valor implícito seria o da regra base
   (`opacity: 0; translateY(1.5rem)`), e a palavra cairia em vez de sumir. */
#S06[data-beat="1"] .words__w:not([data-emph="accent"]) {
  animation: wordOut 400ms var(--ease-in-out) both;
}
@keyframes wordOut {
  from { opacity: 1; transform: none }
  to   { opacity: 0; transform: none }
}

/* ============================================================================
   ATO I — TELA PARTIDA (a composição em si está em worlds.css)
   ========================================================================== */

/* --- S08 · A névoa tem nome · 0:45 -------------------------------------- */
/* As quatro palavras moram DENTRO da faixa de névoa e descem com ela; é o
   backdrop-filter da camada A que as come nas bordas, de graça. */
.fog-words {
  position: absolute; inset-block: 0; left: 50%; width: var(--fog-w);
  transform: translateX(-50%); z-index: 23; overflow: hidden; pointer-events: none;
}
.fog-word {
  position: absolute; left: 50%; top: 0; white-space: nowrap;
  font: 700 var(--fs-h2)/1.2 var(--font-display); color: var(--txt-mid);
  animation: fogWord 14s linear infinite calc(var(--i, 0) * -3.5s);
}
/* -10rem a 72rem cobre o palco inteiro (67,5rem) com folga nas duas pontas. */
@keyframes fogWord {
  from { transform: translate(-50%, -10rem) }
  to   { transform: translate(-50%, 72rem) }
}
/* No clique elas aceleram e saem por baixo. */
#S08[data-beat="1"] .fog-word { animation-duration: 4.5s }

/* --- S09 · A promessa · 0:30 -------------------------------------------- */
/* O card atravessa a névoa: fica ACIMA das três camadas, ao contrário de
   tudo o mais na cena. É o único elemento com essa licença. */
#S09 .promise {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  z-index: 25; width: 56.25rem; min-height: 17.5rem;
  display: grid; align-content: center; gap: var(--sp-2);
  padding: var(--sp-5); text-align: center;
  transition: opacity var(--dur-fast) var(--ease-in-out),
              transform var(--dur-fast) var(--ease-in-out);
}
/* O card sai quando a máquina toma a tela. Corrigido em 11/08: a licença de
   atravessar a névoa valia também para o takeover, e a promessa ficava
   pendurada por cima do Ato II — a plateia lia "eu volto aqui" enquanto a
   tela dizia outra coisa.

   O gatilho é `data-ponte`, escrito pelo script do ato, e NÃO `data-beat`.
   A diferença é a regra dura de §2 do CONTRATO: quem volta da S10 reencontra
   a cena com `data-beat="1"` — porque o motor restaura a última batida — mas
   com a tela partida REPOSTA e a promessa de pé. Preso a `data-beat`, o card
   sumiria justamente na volta, que é quando ele precisa estar lá. */
#S09[data-ponte="1"] .promise {
  opacity: 0; transform: translate(-50%, -50%) scale(.94);
}
.promise__back {
  position: absolute; right: var(--sp-4); bottom: var(--sp-3);
  font: 400 var(--fs-ref)/1 var(--font-mono); color: var(--accent);
}

/* ============================================================================
   ATO II — O MUNDO DA IA
   ========================================================================== */

/* --- S12 · A máquina toma a tela · 0:30 --------------------------------- */
/* Era a S10 até 12/08/2026: a reordenação do Ato II trocou esta cena com a da
   linha do tempo. Os seletores por classe (.formula-band, .rule-30,
   .grid-fluid, .split-arrow) não mudaram — só o id da cena que os hospeda. */
/* A faixa da fórmula ocupa os 30% superiores: top 96px, altura 324px. */
.formula-band {
  position: absolute; left: 0; right: 0; top: 6rem; height: 20.25rem;
  display: grid; place-items: center; z-index: 5;
}
.formula {
  font-size: var(--fs-formula); color: var(--ai-glow); line-height: 1;
  /* drop-shadow e não text-shadow: §5.3 proíbe sombra de texto para destacar
     sobre fundo, e o filtro é o que a lista de animáveis permite. */
  filter: drop-shadow(0 0 3.75rem rgba(125, 226, 230, .35));
}
/* .formula--small era o eco de 56px que abria a S11 enquanto ela vinha DEPOIS
   da fórmula. Com a reordenação de 12/08 a S11 passou a vir antes, e o eco
   viraria spoiler — a regra fica porque a classe é genérica e barata, mas
   hoje ela não tem nenhum uso no deck. */
.formula--small { font-size: var(--fs-h1); opacity: .35 }
/* A régua da linha dos 30% — 96 + 324 = 420px. */
.rule-30 {
  position: absolute; left: 0; right: 0; top: 26.25rem; height: 1px;
  background: rgba(125, 226, 230, .18); z-index: 5;
}
/* Grade fluida 3×3. O offset de ±16px por célula é deliberado: alinhada
   perfeitamente, a grade lê como planilha e mata o clima da cena. */
/* `bottom: --ref-band` e não --margin-v: desde 12/08 esta cena tem faixa de
   provas no pé, e a grade tem de parar acima dela. É a mesma reserva que a
   S26 faz para a citação. */
.grid-fluid {
  position: absolute; left: var(--margin-h); right: var(--margin-h);
  /* + --sp-2 de folga: o offset de +1rem da 8ª célula (Embeddings · RAG)
     encostava a borda dela no topo da faixa. */
  top: 29rem; bottom: calc(var(--provas-band) + var(--sp-2));
  display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, 1fr);
  gap: var(--sp-4); z-index: 4;
}
.grid-fluid > .float {
  position: relative; left: auto; top: auto; width: auto;
  transform: translate(var(--off-x, 0), var(--off-y, 0)) scale(.86);
  animation: none;
}
.grid-fluid > .float:nth-child(1) { --off-x:  1rem; --off-y: -1rem }
.grid-fluid > .float:nth-child(2) { --off-x: -1rem; --off-y:  1rem }
.grid-fluid > .float:nth-child(3) { --off-x:  1rem; --off-y:  1rem }
.grid-fluid > .float:nth-child(4) { --off-x: -1rem; --off-y: -1rem }
.grid-fluid > .float:nth-child(5) { --off-x:  1rem; --off-y:  1rem }
.grid-fluid > .float:nth-child(6) { --off-x: -1rem; --off-y:  1rem }
.grid-fluid > .float:nth-child(7) { --off-x:  1rem; --off-y: -1rem }
.grid-fluid > .float:nth-child(8) { --off-x:  1rem; --off-y:  1rem }
.grid-fluid > .float:nth-child(9) { --off-x: -1rem; --off-y: -1rem }

.split-arrow {
  display: flex; align-items: center; gap: var(--sp-4);
  opacity: 0; transition: opacity var(--dur-base);
}
.scene[data-beat="1"] .split-arrow { opacity: 1 }
.split-arrow__pred { color: var(--primary-soft) }
.split-arrow__gen  { color: var(--ai-core) }
/* A variante da S12 desde 12/08: três definições e um número, no lugar da
   seta preditiva⟶generativa. `space-evenly` e não `center` porque agora são
   quatro blocos de larguras muito diferentes, e centralizados eles encostam. */
.split-arrow--gen { justify-content: space-evenly; gap: var(--sp-3) }
.split-arrow--gen b { font-weight: 800 }
/* A régua tem 1px de altura e o conteúdo transborda para os dois lados a
   partir do centro. Com o stat em --fs-d3 (72px) ele descia até dentro da
   primeira fileira da grade — em --fs-h1 a linha inteira cabe entre a
   fórmula e o `top` da .grid-fluid, que também recuou. */
.gen-stat {
  display: flex; align-items: baseline; gap: var(--sp-2);
  font: 700 var(--fs-h1)/1 var(--font-mono); color: var(--ai-core);
  font-variant-numeric: tabular-nums;
}
#S12 .grid-fluid { top: 31rem }
.gen-stat i {
  font: 500 var(--fs-body)/1.2 var(--font-body); font-style: normal;
  color: var(--txt-low);
}

/* --- S11 · A IA preditiva na medicina · 0:50 · A ESCADA ------------------
   A posição vertical é a FORÇA DA PROVA (--n, de 1 a 4) e a horizontal é o
   ano (--x). É o que faz o desenho contar o argumento antes da fala: a
   escada sobe até o ensaio randomizado e desce no modelo mais recente.

   Absoluto e não grid: os degraus precisam de altura contínua e independente
   da largura da célula, e a haste que desce até a régua tem de nascer do
   card, não da linha. Com grid, o degrau 4 e o degrau 2 dividiriam a mesma
   linha e a altura viraria padding. */
#S11 .escada {
  position: absolute; left: var(--margin-h); right: var(--margin-h);
  top: 11rem; bottom: var(--provas-band);
}
/* A régua da base, de onde saem as hastes. Mesma gramática da .tl da S10 —
   é o único empréstimo visual deliberado entre as duas cenas. */
#S11 .escada::before {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
  background: rgba(255,255,255,.14);
}
#S11 .escada__eixo {
  position: absolute; left: calc(var(--margin-h) - 1.5rem); top: 50%;
  transform: rotate(-90deg) translateX(-50%); transform-origin: left center;
  color: var(--txt-low); white-space: nowrap; z-index: 3;
}
.degrau {
  position: absolute; left: var(--x); width: 17.5%;
  /* 22% por nível: com --n de 1 a 4, o topo do degrau mais alto fica a 88%
     da altura útil e o mais baixo apoia na régua. */
  bottom: calc((var(--n) - 1) * 22%);
  display: grid; gap: .25rem; align-content: start;
  padding: var(--sp-2) var(--sp-3) var(--sp-3);
  border-left: 3px solid var(--stroke-hi);
  background: linear-gradient(180deg, var(--bg-700), transparent 88%);
  transition: opacity var(--dur-base), filter var(--dur-base);
}
/* A haste: desce do card até a régua da base.
   A altura NÃO pode ser `%`: dentro de um pseudo-elemento absoluto, a
   porcentagem se resolve contra a altura do .degrau, não a da .escada — e
   daria uma haste proporcional ao texto do card. Então ela vai em rem, com a
   aritmética escrita: a .escada mede 1080 − 11rem (topo) − 11rem
   (--provas-band) = 45,5rem úteis, e cada nível vale 22% disso ≈ 10rem.
   Quem mexer no `top` da .escada ou em --provas-band remede este 10rem. */
.degrau::after {
  content: ''; position: absolute; left: -3px; top: 100%; width: 3px;
  height: calc((var(--n) - 1) * 10rem);
  background: linear-gradient(180deg, var(--stroke-hi), transparent);
}
.degrau__ano  { font: 700 var(--fs-h2)/1 var(--font-mono); color: var(--primary-soft) }
.degrau__nome { font: 800 var(--fs-h1)/1.05 var(--font-display); color: var(--txt-high);
                letter-spacing: var(--tr-h1) }
.degrau__prova{ font: 500 var(--fs-body)/1.25 var(--font-body); color: var(--txt-mid) }
/* "AUC" como rótulo em bloco, e não na mesma linha: "AUC 0,82–0,92" em mono
   a 44px pede 338px e o degrau tem 254px úteis — quebrava no meio do número.
   Sozinho, "0,82–0,92" mede 234px e cabe. */
.degrau__stat { font: 700 var(--fs-h2)/1 var(--font-mono); color: var(--txt-low);
                font-variant-numeric: tabular-nums; white-space: nowrap }
.degrau__stat i {
  display: block; font: 700 var(--fs-label)/1.4 var(--font-display);
  font-style: normal; letter-spacing: var(--tr-label); color: var(--txt-low);
}
.degrau[data-emph="accent"] { border-left-color: var(--accent) }
.degrau[data-emph="accent"]::after { background: linear-gradient(180deg, var(--accent), transparent) }
.degrau[data-emph="accent"] .degrau__ano  { color: var(--accent) }

/* O selo do SCREEN só entra na batida — é o clímax da cena, e é a única coisa
   que a batida ACRESCENTA (CONTRATO §2, segunda regra dura). */
.degrau__selo {
  /* --r-md e não --r-full: em três linhas, a pílula deixa de ser pílula e o
     texto sai pela curva. Retângulo de canto suave aguenta a quebra. */
  margin-top: var(--sp-1); padding: var(--sp-1) .75rem;
  border: 1px solid var(--accent); border-radius: var(--r-md);
  font: 700 var(--fs-label)/1.3 var(--font-display);
  letter-spacing: var(--tr-label); color: var(--accent); text-transform: uppercase;
  opacity: 0; transform: translateY(.5rem);
  transition: opacity var(--dur-base), transform var(--dur-base) var(--ease-out-expo);
}
#S11[data-beat="1"] .degrau__selo { opacity: 1; transform: none }
/* E os outros recuam, para o olho ir ao topo da escada. */
#S11[data-beat="1"] .degrau:not([data-key="1"]) { opacity: .38; filter: grayscale(.5) }
.duo__col {
  min-height: 38.75rem; display: grid; align-content: start; gap: var(--sp-3);
  transform-origin: center;
  transition: filter var(--dur-base), transform var(--dur-base) var(--ease-out-expo);
}
.duo__col svg { width: 3.5rem; height: 3.5rem }
.duo__col--pred svg { color: var(--primary-soft) }
.duo__col--gen  svg { color: var(--ai-core) }
.duo__stat { font: 700 var(--fs-72m)/1 var(--font-mono); font-variant-numeric: tabular-nums }
.duo__col--pred .duo__stat { color: var(--primary-soft) }
.duo__col--gen  .duo__stat { color: var(--ai-core) }
/* A cena tem DOIS estados de ênfase, e não um estado neutro seguido de um
   destaque. Batida 0: o palestrante mora no mundo preditivo — a generativa
   está em tela, legível, mas recuada. Batida 1: inverte, e a generativa
   assume. Foi assim que a reordenação de 12/08 transformou uma comparação
   estática na travessia de um mundo para o outro.
   A coluna que cresce cresce por transform, não por grid-template-columns:
   largura não entra em animação neste deck. */
#S11[data-beat="0"] .duo__col--gen  { filter: grayscale(.7) opacity(.4) }
#S11[data-beat="0"] .duo__col--pred { transform: scale(1.04); z-index: 2 }
#S11[data-beat="1"] .duo__col--pred { filter: grayscale(.7) opacity(.4) }
#S11[data-beat="1"] .duo__col--gen  { transform: translateX(-8%) scale(1.08); z-index: 2 }

/* --- S12 · 1950 → 2017 · 0:30 · data-cut="1" ---------------------------- */
#S10 .tl { position: absolute; left: 12rem; right: 12rem; top: 33.75rem; height: 0; z-index: 3 }
.tl::before {
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: 2px;
  background: rgba(255,255,255,.14);
}
.tl__mark {
  position: absolute; left: var(--x); top: 0; transform: translateX(-50%);
  transition: transform var(--dur-fast) var(--ease-in-out), opacity var(--dur-fast);
  transition-delay: calc(var(--i, 0) * 40ms);
}
.tl__node {
  width: 1.5rem; height: 1.5rem; border-radius: var(--r-full);
  background: var(--txt-low); transform: translateY(-50%);
}
.tl__label {
  position: absolute; left: 50%; transform: translateX(-50%); width: 15rem;
  text-align: center; font: 500 var(--fs-body)/1.2 var(--font-body); color: var(--txt-mid);
}
.tl__mark:nth-child(odd)  .tl__label { bottom: 2.5rem }
.tl__mark:nth-child(even) .tl__label { top: 2.5rem }
.tl__mark[data-key="1"] .tl__node {
  background: var(--accent); transform: translateY(-50%) scale(2);
}
.tl__mark[data-key="1"] .tl__label { color: var(--txt-high); font-weight: 700 }
.tl__curve { fill: none; stroke-width: 6; stroke-linecap: round }
/* Todos os marcos anteriores a 2017 colapsam para dentro do de 2017. */
#S10[data-beat="1"] .tl__mark:not([data-key="1"]) {
  transform: translateX(-50%) scale(0); opacity: 0;
}

/* --- S13 · O morph: fórmula → rede neural · 0:55 · assinatura ----------- */
#S13 .kqv { color: var(--accent) }
.callout {
  font: 500 var(--fs-body)/1.3 var(--font-body); color: var(--txt-mid);
  transition: opacity 200ms;
}
/* A régua dos 30% e os três callouts pertencem à fórmula, não à rede: depois
   do morph a régua vira um traço horizontal atravessando o meio do desenho.
   Some junto com os callouts, na mesma janela de 200 ms. */
#S13 .rule-30 { transition: opacity 200ms }
/* A faixa de provas entra na lista: nada divide a tela com o morfismo. Ela
   desce junto com a régua e os callouts, na mesma janela de 200 ms. */
#S13 .provas { transition: opacity 200ms }
#S13[data-beat="1"] .rule-30,
#S13[data-beat="1"] .provas,
#S13[data-beat="1"] .callout { opacity: 0 }
/* Canvas das ~3.400 partículas. Vive por cima da fórmula e some no handoff. */
/* width/height explícitos porque <canvas> é elemento SUBSTITUÍDO: inset:0
   sozinho não o estica — com width:auto ele fica no tamanho intrínseco de
   300×150 no canto, e o morph desenha em 0 pixels visíveis. Foi exatamente o
   que aconteceu até 11/08: morph:start nunca disparava e a cena de assinatura
   rodava sem a sua coreografia, mascarada em headless pelo caminho instantâneo
   do prefers-reduced-motion. */
.morph-canvas { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 6; pointer-events: none }

/* A rede neural viva (SVG, depois do handoff — §3.3, passo 4). */
.net { display: block; width: 68.75rem; margin: 0 auto }
.net-edge  { stroke: var(--primary-soft); stroke-width: 2; stroke-opacity: .35; fill: none }
.net-pulse {
  stroke: var(--ai-glow); stroke-width: 3; stroke-linecap: round; fill: none;
  stroke-dasharray: 26 900; stroke-dashoffset: 926;
  animation: netFlow 2.2s linear infinite calc(var(--i, 0) * -.11s);
}
@keyframes netFlow { to { stroke-dashoffset: 0 } }
/* O nó respira por transform, não por animação do atributo r: geometria de
   SVG não está na lista de propriedades animáveis do projeto. */
.net-node {
  fill: var(--bg-800); stroke: var(--ai-glow); stroke-width: 2.5;
  transform-box: fill-box; transform-origin: center;
  animation: netBreathe 3.4s ease-in-out infinite calc(var(--i, 0) * -.2s);
}
@keyframes netBreathe { 50% { transform: scale(1.16); stroke-opacity: 1 } }
.net--small { transform: scale(.4); transform-origin: right center }

/* O SÍMBOLO "O TRANSFORMADOR" (§3.6) — desenho ORIGINAL nosso.
   A marca da Hasbro nunca é desenhada: a piada é falada. Os três chevrons
   são Q, K e V — e a legenda de baixo é o easter egg da QKV Works, plantado
   na S02 ("vocês vão entender daqui a dez minutos") e colhido aqui. */
.tf-wrap { perspective: 56.25rem }
.tf-mark { display: block; width: 13.75rem; height: 13.75rem; margin: 0 auto }
.tf-shell {
  stroke-dasharray: 640; stroke-dashoffset: 640;
  animation: tfDraw .55s cubic-bezier(.65, 0, .35, 1) forwards;
}
.tf-half {
  transform-box: fill-box; transform-origin: center left; opacity: 0;
  animation: tfUnfold .5s .35s var(--ease-overshoot) forwards;
}
.tf-r { transform-origin: center right }
.tf-seam {
  stroke-dasharray: 190; stroke-dashoffset: 190;
  animation: tfDraw .4s .55s ease-out forwards;
}
/* Desenha e, em repouso, pulsa em cascata: parece um sinal descendo pelas
   camadas. Longhand porque são duas animações com atrasos diferentes. */
.tf-chev path {
  stroke-dasharray: 110; stroke-dashoffset: 110;
  animation-name: tfDraw, tfChev;
  animation-duration: .34s, 2.4s;
  animation-delay: calc(.72s + var(--i, 0) * .11s), calc(1.1s + var(--i, 0) * .2s);
  animation-timing-function: cubic-bezier(.65, 0, .35, 1), ease-in-out;
  animation-iteration-count: 1, infinite;
  animation-fill-mode: forwards, none;
}
@keyframes tfDraw   { to { stroke-dashoffset: 0 } }
@keyframes tfUnfold { from { transform: rotateY(78deg); opacity: 0 }
                      to   { transform: rotateY(0);     opacity: 1 } }
@keyframes tfChev   { 50% { opacity: .6 } }
.tf-legend {
  text-align: center; margin-top: var(--sp-2);
  font: 600 var(--fs-label)/1 var(--font-mono);
  letter-spacing: .28em; color: var(--txt-low);
}

/* A fileira dos nove chips minimizados não é rodapé nesta cena: é o terço
   inferior da composição — símbolo, rede, e embaixo os nove nomes que a
   plateia reconhece. Ancorada pelo topo (50rem = 800px) porque a altura dela
   depende de quantas fileiras o wrap produzir, e é o topo que precisa ficar
   à distância certa da rede. O teto de 80rem força a quebra em 5 + 4: em
   1728px de largura útil o wrap natural daria 7 + 2, que lê como sobra.
   A fileira é o desfecho da cena e não infraestrutura, então não herda o
   .35 de opacidade da S22. */
#S13 .infra-row {
  top: 50rem; bottom: auto;
  max-width: 80rem; margin-inline: auto;
  flex-wrap: wrap; row-gap: var(--sp-2);
  opacity: 1;
}
#S13 .infra-row .float[data-min="1"] { opacity: .8 }

/* --- S14 · Prompt e contexto · 0:45 ------------------------------------- */
/* O FILME DA BATIDA 1 (14/08): cobre o palco enquanto toca e some por
   opacidade quando acaba, revelando o estado final REAL da cena — que o
   [data-beat="1"] já aplicou por baixo. Os dois extremos do arquivo são
   screenshots da própria cena (batida 0 e batida 1), então entrada e saída
   são sem costura. pointer-events none sempre: o filme é pintura, não UI.
   Se ele não tocar (arquivo ausente, autoplay barrado, movimento reduzido),
   fica em opacity 0 e a coreografia CSS de 13/08 segue sendo o que se vê. */
#S14 .s14-filme {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; z-index: 80; opacity: 0; pointer-events: none;
  transition: opacity 240ms ease-out;
}
#S14 .s14-filme[data-on="1"] { opacity: 1 }

/* A TARJA ÂMBAR EM 36px, e não nos 44 da regra global (13/08/2026). O texto
   dela cresceu de 34 para 84 caracteres quando ela deixou de dizer só o limite
   ("o modelo não sabe do seu paciente") e passou a dizer a mecânica. Em 44px
   isso quebra em DUAS linhas, a tarja engorda de 100 para 154px e come 75px do
   cartão do grounding — que é o terceiro bloco da cena e o argumento do
   slide. Em 36px (acima do piso de 32 de §5.1) o texto cabe numa linha: 84
   caracteres × ~18px = ~1512px contra 1728 úteis.
   Medido na sonda: a tarja passa a ocupar 911..979 e o cartão termina em 914 —
   3px de encosto, contra os 26px que a versão de 44px já encostava antes.
   É a mesma saída que a S19B usa, pela mesma razão. */
#S14 .band-warn { font-size: var(--fs-lg); padding: var(--sp-2) var(--margin-h) }

/* Etiqueta lateral vertical dos três blocos. */
.tagged { position: relative; padding-left: 3.5rem }
.tagged__tag {
  position: absolute; left: 0; top: 0; bottom: 0; width: 2.25rem;
  display: grid; place-items: center; border-radius: var(--r-sm);
  writing-mode: vertical-rl; text-orientation: mixed;
  font: 600 var(--fs-label)/1 var(--font-body);
  letter-spacing: var(--tr-label); text-transform: uppercase;
}
.tagged--ai  .tagged__tag { background: var(--ai-core); color: var(--bg-900) }
.tagged--pri .tagged__tag { background: var(--primary); color: var(--txt-high) }
.tagged--acc .tagged__tag { background: var(--accent);  color: var(--txt-high) }
.code-block {
  background: var(--bg-800); border: 1px solid var(--stroke);
  border-radius: var(--r-md); padding: var(--sp-3);
  font: 400 var(--fs-body)/1.4 var(--font-mono); color: var(--txt-mid);
}
.pill-row { display: flex; flex-wrap: wrap; gap: var(--sp-2) }
.pill {
  padding: var(--sp-1) var(--sp-3); border-radius: var(--r-full);
  border: 1px solid var(--stroke-hi); color: var(--txt-mid);
  font: 500 var(--fs-body)/1.2 var(--font-body); white-space: nowrap;
}

/* A COREOGRAFIA "DENTRO DA MÁQUINA" (13/08/2026, determinação do palestrante).
   Batida 1: os três blocos da esquerda se FUNDEM e entram na boca da rede
   (--fx/--fy vêm do script da cena, medidos no scene:enter com transforms
   neutralizados); a rede cresce de 40% para 85% e desliza ao centro; as
   conexões aceleram; e na saída materializa-se a .resposta — com fonte,
   porque é a cena do grounding. Batida 2: a tarja âmbar (a regra global de
   .band-warn dispara na 1, e aqui o par de regras da S19B a segura até a 2).

   ESTADOS declarativos por [data-beat], nunca por timer: voltar da S15
   reencontra a cena assentada sem reencenar (CONTRATO §2). Os delays vivem
   SÓ nos estados de batida — a regra base transita sem delay, e é ela que
   rege o caminho de volta quando o palestrante retrocede uma batida.
   A coreografia inteira fecha em ~1,8 s: dentro dos 3,6 s que o passe de
   estado do verifica-ida-volta espera para fotografar. */
#S14 { --s14-net-vivo: translate(-30rem, 5rem) scale(.85) }
#S14 .s14-entrada { position: relative; z-index: 2 }
#S14 .s14-entrada .tagged {
  /* Sem os headings (reforma de 13/08) o miolo de cada bloco esticava na
     track do .u-stack (grid, align-content: stretch por padrão) — code-block
     de 2 linhas com 270px de altura. Centrado, o conteúdo fica com a altura
     natural e os três blocos seguem dividindo a coluna. */
  align-content: center;
  transition: transform var(--dur-base) var(--ease-in-out),
              opacity var(--dur-fast) linear;
}
#S14[data-beat="1"] .s14-entrada .tagged,
#S14[data-beat="2"] .s14-entrada .tagged {
  transform: translate(var(--fx, 0px), var(--fy, 0px)) scale(.08);
  opacity: 0;
  /* o fade começa a 55% do trajeto: o bloco ainda é legível ao sair do lugar
     e já é ponto de luz ao tocar a boca da rede */
  transition: transform .55s var(--ease-in-out), opacity .3s linear .3s;
}
#S14[data-beat="1"] .s14-entrada .tagged:nth-child(2),
#S14[data-beat="2"] .s14-entrada .tagged:nth-child(2) { transition-delay: .08s, .38s }
#S14[data-beat="1"] .s14-entrada .tagged:nth-child(3),
#S14[data-beat="2"] .s14-entrada .tagged:nth-child(3) { transition-delay: .16s, .46s }

/* A rede cresce quando os blocos chegam à boca (delay = duração da fusão). */
#S14 .net--small { transition: transform var(--dur-slow) var(--ease-out-expo) }
#S14[data-beat="1"] .net--small,
#S14[data-beat="2"] .net--small { transform: var(--s14-net-vivo); transition-delay: .45s }
/* Conexões em ritmo de trabalho: os pulsos correm 2× (2,2s → 1,1s) e os nós
   respiram no dobro do passo. Mudar duração de animação em curso não a
   reinicia — só re-mapeia o tempo. */
#S14[data-beat="1"] .net-pulse, #S14[data-beat="2"] .net-pulse { animation-duration: 1.1s }
#S14[data-beat="1"] .net-node,  #S14[data-beat="2"] .net-node  { animation-duration: 1.7s }

/* A resposta, ancorada na saída da rede. 26rem: cabe nos ~30rem que a rede
   transladada deixa livres à direita (borda dela em ~84rem do palco). */
#S14 .resposta {
  position: absolute; right: 0; top: 50%; width: 26rem;
  transform: translateY(-50%) translateX(var(--sp-5));
  opacity: 0;
  box-shadow: var(--glow-ai);
  transition: transform var(--dur-base) var(--ease-out-expo),
              opacity var(--dur-base) linear;
}
#S14[data-beat="1"] .resposta,
#S14[data-beat="2"] .resposta { transform: translateY(-50%); opacity: 1; transition-delay: 1.2s }

/* A tarja espera a batida 2 — mesmo par de regras da S19B. */
#S14[data-beat="1"] .band-warn { opacity: 0; transform: translateY(100%) }
#S14[data-beat="2"] .band-warn { opacity: 1; transform: none }

/* Movimento reduzido: a coreografia vira salto seco. É também o que o
   verifica-ida-volta fotografa no passe de pixel (260 ms de espera — uma
   transição de 550 ms aqui faria o print pegar o quadro do meio). */
@media (prefers-reduced-motion: reduce) {
  #S14 .s14-entrada .tagged, #S14 .net--small, #S14 .resposta { transition: none !important }
}
:root[data-motion="reduced"] #S14 .s14-entrada .tagged,
:root[data-motion="reduced"] #S14 .net--small,
:root[data-motion="reduced"] #S14 .resposta { transition: none !important }

/* Faixa âmbar que atravessa a base numa batida (S14 e S19B). */
.band-warn {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 30;
  background: var(--warn); color: var(--bg-800);
  padding: var(--sp-3) var(--margin-h);
  font: 600 var(--fs-h2)/1.2 var(--font-body);
  opacity: 0; transform: translateY(100%);
  transition: opacity var(--dur-fast), transform var(--dur-fast) var(--ease-out-expo);
}
.scene[data-beat="1"] .band-warn { opacity: 1; transform: none }

/* --- S15 · O que eles são hoje (e o que eles erram) · 0:40 · cut 2 ------ */
#S15 .chips-row { display: flex; gap: var(--sp-3); justify-content: center; align-items: center }
#S15 .chips-row .brand-chip { height: 6rem; padding: 0 var(--sp-4); font-size: var(--fs-h2) }
/* ⚑ O balão de destaque do OpenEvidence tem espaço reservado, mas os números
   que o roteiro sugere (~45% dos médicos dos EUA, >20 mi de consultas/mês,
   +38% do "não sei" e o 43%→99% do RAG) estão em §4.3 da lista de números
   aprovados — SEM ENTRADA. Enquanto não migrarem para a lista branca, o
   balão só pode conter a ordem de grandeza dita em voz alta, sem dígito. */
.balloon-acc {
  border: 2px solid var(--accent); border-radius: var(--r-lg);
  background: rgba(230,57,70,.10); padding: var(--sp-2) var(--sp-3);
  font: 600 var(--fs-body)/1.2 var(--font-body); color: var(--accent-soft);
}
#S15 .defenses { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: var(--sp-4) }
#S15 .defenses > * { display: grid; grid-template-columns: 2rem 1fr; gap: var(--sp-2); align-items: start }
#S15 .defenses svg { width: 2rem; height: 2rem; color: var(--accent) }

/* ============================================================================
   ATO III — O MUNDO DA ASSISTÊNCIA
   Reset visual: sai o vidro emissivo, entra o papel.
   ========================================================================== */

/* --- S16 · De volta ao chão · 0:20 -------------------------------------- */
#S16, #S17, #S18, #S19 {
  background:
    repeating-linear-gradient(0deg, transparent 0 2.4375rem, var(--med-rule) 2.4375rem 2.5rem),
    var(--med-bg);
}
/* A silhueta do leito se desenha em 3 s e fica. Discreta de propósito: é
   cenário, não conteúdo. */
.bed-silhouette {
  position: absolute; inset: 0; z-index: 0; opacity: .10; pointer-events: none;
}
.bed-silhouette path {
  fill: none; stroke: var(--med-ink); stroke-width: 2;
  stroke-dasharray: var(--len, 2400); stroke-dashoffset: var(--len, 2400);
  animation: strokeDraw 3s ease-out forwards;
}
#S16 .s16__body { position: relative; z-index: 1 }

/* --- S17 · O que é sepse · 0:25 · data-cut="3" -------------------------- */
#S17 .def { max-width: 84rem }
#S17 .def em { font-style: normal; color: var(--accent) }
.micro-row { display: flex; gap: var(--sp-4); flex-wrap: wrap }
.micro-card {
  border: 1px solid var(--med-border); border-radius: var(--r-sm);
  background: var(--med-surface); color: var(--med-ink);
  padding: var(--sp-2) var(--sp-3);
  font: 500 var(--fs-body)/1.2 var(--font-body);
}
/* Chip de deferência (S17, S28). A classe carrega só a APARÊNCIA — o lugar é
   de quem usa. O canto inferior direito não está vago: é onde mora .scene__ref
   (base.css), que é absoluta com left E right nas margens, ocupa os 108rem da
   área segura e CRESCE PARA CIMA a cada linha. O chip ancorado em --margin-v
   cruzava a primeira linha da citação nas DUAS cenas que o usam (medido:
   514×26px na S17, 700×28px na S28) e apagava prova de fonte, o que a regra 7
   de §5.3 não admite. Por isso: o padrão é em fluxo, na coluna de conteúdo
   (S17); o canto virou a variante --corner, ancorada em --ref-band — a faixa
   que a citação de duas linhas reivindica (token em tokens.css).
   Fundo opaco + --txt-mid (11,4:1) em vez de --txt-low (6,4:1): nunca mais
   texto sob texto, e o contraste passa a cumprir os 7:1 de §5.2. */
.chip-callback {
  display: inline-block; width: max-content;
  border: 1px solid var(--stroke-hi); border-radius: var(--r-full);
  padding: var(--sp-1) var(--sp-3); color: var(--txt-mid);
  background: var(--bg-900);
  font: 500 var(--fs-body)/1.2 var(--font-body);
}
.chip-callback--corner {
  /* --provas-band basta na S17, que tem faixa sem nota; a S28 sobrescreve
     logo abaixo, porque lá existe a linha do Monte Carlo por cima da faixa. */
  position: absolute; right: var(--margin-h); bottom: var(--provas-band);
  max-width: calc(100% - var(--margin-h) * 2);
}

/* --- S18 · A tela vira o protocolo · 0:50 · NUNCA CORTAR ---------------- */
/* Quatro faixas de 200px. A cena é um painel operacional, não um slide. */
/* As faixas eram quatro trilhas FIXAS de 12,5rem. Trilha fixa não segura
   conteúdo maior: a faixa do bundle mede 365px de conteúdo e transbordava 67px
   sobre a faixa seguinte — cartão em cima de cartão, "GATILHO" impresso sobre
   "BUNDLE · 1ª HORA" e os quatro indicadores empurrados para debaixo das
   caixas. Trilha `auto` com o container ancorado nas duas pontas: cada faixa
   toma a altura que precisa e o que sobra fica distribuído entre elas. */
/* As quatro faixas somavam 897px de altura natural para 888px de área útil, e
   a legenda da última ("coordenação · relatório mensal") entrava na referência
   de três linhas do rodapé. A folga veio de composição, não de tipografia — o
   piso de 32px de §4.3 é o que garante a leitura da última fileira:
     · a referência virou uma linha (o título completo do roteiro do ILAS e o
       pacote global de 2023 passaram para a fala, em <details class="notes">);
     · a faixa de reavaliação virou linha única (.bundle--inline): eram duas
       caixas altas com um rótulo e um prazo cada, quase vazias por dentro;
     · o respiro das faixas ficou assimétrico — o filete pertence à faixa que
       vem abaixo dele, então tem ar acima e pouco abaixo.
   Sobram ~56px, que o space-between distribui entre as quatro. */
#S18 .prot {
  position: absolute; left: var(--margin-h); right: 8rem;
  /* --ref-band, e não --provas-band-nota: esta cena usa a faixa COMPACTA
     (ver #S18 --faixa-h). Com a faixa cheia, as quatro faixas do protocolo
     somam mais que a altura restante e o painel transborda. */
  top: 7.5rem; bottom: var(--ref-band);
  display: grid; grid-template-rows: repeat(4, auto); align-content: space-between;
}
.prot__band {
  border-top: 1px solid var(--med-border);
  display: grid; align-content: center; gap: var(--sp-2);
  padding: var(--sp-2) 0 var(--sp-1);
}
.prot__band > .t-label { color: var(--med-stamp) }
.crit-row { display: flex; gap: var(--sp-2); flex-wrap: wrap }
.crit-pill {
  border: 1px solid var(--med-border); border-radius: var(--r-full);
  padding: var(--sp-1) var(--sp-3); color: var(--med-ink);
  font: 500 var(--fs-body)/1.2 var(--font-mono); white-space: nowrap;
}
.crit-pill[data-on="1"] {
  border-color: var(--accent); color: var(--accent-soft);
  background: rgba(230,57,70,.12);
}
/* auto-flow em coluna: o bundle da S18 tem 5 caixas e o da demo tem 6
   (paciente-demo.json manda no catálogo de etapas). O mesmo CSS serve aos dois. */
.bundle { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: var(--sp-3) }
.bundle__item {
  position: relative;
  border: 1px solid var(--med-border); border-radius: var(--r-sm);
  background: var(--med-surface); color: var(--med-ink);
  padding: var(--sp-2) var(--sp-3);
  display: grid; gap: var(--sp-1); align-content: center;
}
.bundle__item svg { width: 2rem; height: 2rem; color: var(--med-ink) }
/* Na S18 o ícone divide a linha com o rótulo, em vez de ocupar uma linha só
   para si — é o mesmo arranjo que o painel da S25 já usa. São 40px por caixa,
   80px nas duas faixas de bundle, e é exatamente o que faltava para as quatro
   faixas caberem na altura do palco sem uma invadir a outra. */
#S18 .bundle__item { grid-template-columns: 2rem 1fr; column-gap: var(--sp-2) }
#S18 .bundle__item > svg { grid-column: 1; grid-row: 1; align-self: center }
#S18 .bundle__item > :not(svg) { grid-column: 2 }
/* Reavaliação: dois itens sem legenda de quem registra. Rótulo e prazo na
   mesma linha — a caixa deixa de ser moldura alta em torno de duas palavras. */
#S18 .bundle--inline .bundle__item { grid-template-columns: 2rem 1fr auto; align-items: center }
#S18 .bundle--inline .bundle__timer { grid-column: 3; grid-row: 1 }
.bundle__label { font: 500 var(--fs-body)/1.2 var(--font-body) }
.bundle__timer { font: 700 var(--fs-h2)/1 var(--font-mono); font-variant-numeric: tabular-nums }
.bundle__item[data-state="done"] { border-color: var(--ok) }
.bundle__item[data-state="done"] svg { color: var(--ok) }
.bundle__item[data-state="late"] { border-color: var(--accent) }
.bundle__item[data-state="late"] .bundle__timer { color: var(--accent) }
/* O piscar da etapa vencida é opacidade de uma borda extra — nunca do texto,
   que tem que continuar legível do fundo da sala. */
.bundle__item[data-state="late"]::after {
  content: ''; position: absolute; inset: -2px; border-radius: var(--r-sm);
  border: 2px solid var(--accent); animation: critBlink 1.4s ease-in-out infinite;
}
@keyframes critBlink { 50% { opacity: .15 } }
/* Quem registra cada coisa e onde — entra na batida 1. */
.prot__who {
  font: 500 var(--fs-body)/1.2 var(--font-body); color: var(--txt-low);
  opacity: 0; transform: translateY(.5rem);
  transition: opacity var(--dur-fast), transform var(--dur-fast) var(--ease-out-expo);
  transition-delay: calc(var(--i, 0) * 60ms);
}
#S18[data-beat="1"] .prot__who { opacity: 1; transform: none }
.kpi-row { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: var(--sp-4) }
.kpi__num {
  font: 700 var(--fs-kpi)/1 var(--font-mono); font-variant-numeric: tabular-nums;
  color: var(--med-ink);
}
.kpi__label { font: 500 var(--fs-body)/1.2 var(--font-body); color: var(--txt-low) }
/* Enquanto os números institucionais reais não chegarem (pendência 8 da
   PARTE 7), o asterisco é obrigatório e não é decoração. */
.is-illustrative::after { content: '*'; color: var(--warn) }
.prot__rail {
  position: absolute; right: 0; top: 7.5rem; bottom: calc(var(--margin-v) + var(--sp-2)); width: .5rem;
  border-radius: var(--r-full); background: var(--med-slab-2);
}
.prot__rail-fill {
  width: 100%; border-radius: var(--r-full); background: var(--med-stamp);
  height: var(--p, 25%);
}

/* --- S19 · Onde o tempo se perde · 0:45 · NUNCA CORTAR ------------------ */
/* REFORMA DE 10/08: a cena virou monotemática (zona cega ILAS). A .timebar do
   dia do intensivista migrou para a S19B — as regras dela estão no Ato V. */
/* A linha do tempo da zona cega. Proporções exatas 2,7 : 1,0 → 73% / 27%. */
#S19 .blindline { width: 90rem; margin: 0 auto }
#S19 .blindline__bar {
  height: 7.5rem; border-radius: var(--r-md);
  overflow: hidden; display: flex;
}
#S19 .blindline__blind {
  flex: 0 0 73%; display: grid; place-items: center;
  background: var(--med-slab-2); color: var(--txt-high);
  font: 600 var(--fs-body)/1.2 var(--font-body);
}
/* --ok de propósito: a única parte verde é a única parte que alguém mede.
   Texto em --bg-900 porque --txt-high sobre o verde não alcança os 7:1. */
#S19 .blindline__meas {
  flex: 0 0 27%; display: grid; place-items: center;
  background: var(--ok); color: var(--bg-900);
  font: 600 var(--fs-body)/1.2 var(--font-body);
  border-left: 3px solid var(--warn);
}
/* O eixo sob a barra: t₀ na fronteira dos 73%, e a legenda do trecho medido
   centrada sob os 27% restantes — em duas alturas para não colidirem. */
#S19 .blindline__axis {
  display: grid; grid-template-columns: 73% 27%;
  margin-top: var(--sp-2);
}
#S19 .blindline__t0 {
  grid-column: 1; justify-self: end; transform: translateX(50%);
  font: 700 var(--fs-body)/1.2 var(--font-mono); color: var(--warn);
}
#S19 .blindline__span {
  grid-column: 2; justify-self: center; margin-top: var(--sp-5);
  font: 500 var(--fs-body)/1.2 var(--font-body); color: var(--txt-mid);
}
#S19 .ratio { font: 800 var(--fs-d1)/var(--lh-d1) var(--font-mono); color: var(--accent) }
/* Tarja da batida 1 — aqui, a enfermaria pública sobre a barra nacional. */
.tarja-acc {
  position: absolute; left: 0; right: 0; top: var(--tarja-top, 24rem); z-index: 30;
  background: var(--accent); color: var(--txt-high);
  padding: var(--sp-3) var(--margin-h);
  opacity: 0; transform: translateY(-100%);
  transition: opacity var(--dur-fast), transform var(--dur-base) var(--ease-out-expo);
}
.scene[data-beat="1"] .tarja-acc { opacity: 1; transform: none }

/* --- S20 · O preço · 0:20 · data-cut="1" -------------------------------- */
#S20 .price { display: grid; place-content: center; gap: var(--sp-3); height: 100%; text-align: center }
#S20 .price__num { font-family: var(--font-mono); color: var(--accent) }

/* ============================================================================
   ATO IV (S21–S23 + a demo) E ATO V (S19B–S29) — O ENCONTRO E O QUE MAIS EXISTE
   (tela partida, névoa e entrelaçamento estão em worlds.css)
   ========================================================================== */

/* --- S21 · A tela inicial ressurge · 0:20 · NUNCA CORTAR ---------------- */
#S21 .back-mark {
  position: absolute; left: 50%; bottom: var(--margin-v); transform: translateX(-50%);
  z-index: 40; font: 700 var(--fs-body)/1 var(--font-mono); color: var(--accent);
}

/* --- S22 · O entrelaçamento · 0:35 · NUNCA CORTAR ----------------------- */
#S22 .s22__title { position: absolute; left: var(--margin-h); top: var(--margin-v); z-index: 40 }
/* Os wordmarks que não formam par descem para a fileira de infraestrutura. */
.infra-row {
  position: absolute; left: var(--margin-h); right: var(--margin-h); bottom: var(--margin-v);
  display: flex; gap: var(--sp-3); justify-content: center; opacity: .35; z-index: 35;
}

/* --- S23 · Reconhecimento precoce + estratificação (F1) · 0:55 ---------- */
.evidence { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gutter) }
.evidence__num { font: 800 var(--fs-d3)/1 var(--font-display); letter-spacing: var(--tr-d3) }
.evidence__num--pri { color: var(--primary-soft) }
.evidence__num--acc { color: var(--accent) }
.evidence__note { font: 400 var(--fs-ref)/1.3 var(--font-mono); color: var(--txt-dim) }

.flow { display: flex; align-items: stretch; gap: var(--sp-1) }
.flow__step {
  flex: 1; display: grid; align-content: center; gap: var(--sp-1);
  border: 1px solid var(--stroke); border-radius: var(--r-md);
  padding: var(--sp-3); font: 500 var(--fs-body)/1.2 var(--font-body);
  color: var(--txt-mid);
}
.flow__arrow { flex: 0 0 2rem; display: grid; place-items: center; color: var(--txt-low) }
.flow__step--suggest { border-color: var(--ai-edge); color: var(--txt-high) }
/* A última caixa é a tese inteira da palestra em um retângulo: o protocolo
   só abre com ação humana. Ela é a ÚNICA em --accent nesta cena. */
.flow__step--lock {
  background: var(--accent); border-color: var(--accent); color: var(--txt-high);
  font-weight: 600;
}
.flow__step--lock svg { width: 2.5rem; height: 2.5rem }
.seal-cfm {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  border: 1px solid var(--stroke-hi); border-radius: var(--r-sm);
  padding: var(--sp-1) var(--sp-2);
  font: 500 var(--fs-body)/1.2 var(--font-body); color: var(--txt-mid);
}
.ruler {
  display: flex; align-items: center; gap: var(--sp-3);
  opacity: 0; transform: translateY(1rem);
  transition: opacity var(--dur-fast), transform var(--dur-fast) var(--ease-out-expo);
}
.scene[data-beat="1"] .ruler { opacity: 1; transform: none }
.ruler__tick {
  width: 1rem; height: 1rem; border-radius: var(--r-full);
  background: var(--txt-dim);
}
.ruler__tick[data-on="1"] { background: var(--accent) }

/* --- S23 · a faixa de base e a reserva do rodapé de três linhas ---------
   O .scene__ref é ancorado em `bottom: calc(var(--margin-v) - 2.5rem)` — 2rem
   da base, 2,5rem ABAIXO da linha de margem segura. Isso foi desenhado para
   UMA linha. Aqui são TRÊS (a citação é prova de fonte e não encolhe): o
   bloco mede 101,4px e seu topo sobe a y≈944, invadindo ~61px da área em que
   o corpo termina (y=1008) — a régua imprimia por cima da citação em toda a
   largura.
   O corpo NUNCA esteve saturado: o conteúdo natural mede 649px em 792px
   disponíveis. Ele estava com a base PREGADA em y≈1005 pelo height:100% da
   grade (align-content: stretch estica as faixas auto). É o padding-bottom
   que puxa a base para y=909, com 34,6px de folga até a citação — e a reserva
   vale 1:1 (varrida de 0 a 176px, cada 8px sobem o corpo 8px, sem transbordo
   até ~162px).
   Se a citação ganhar uma QUARTA linha, o topo dela cai para y≈910 e esta
   reserva deixa de cobrir: suba para 8–9rem (128–144px), medido, não no olho.
   A media query existe porque no mobile --margin-v é 0 sem unidade e um calc
   com ele seria inválido; abaixo de 901px a cena é fluxo e a reserva não é
   necessária. */
#S23 .s23__base {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: var(--sp-3) var(--sp-6);
}
#S23 .s23__base > .seal-cfm { flex: none }
@media (min-width: 901px) { #S23 > .u-stack { padding-bottom: var(--sp-7) } }

/* --- S19B · Suporte à decisão + protocolos inteligentes (F2, F3) · 1:00 -- */
#S19B .duo2, #S29 .duo2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-6); height: 100% }

/* REFORMA DE 10/08 — a barra do dia do intensivista (ex-S19) abre a cena como
   justificativa das duas ferramentas. Mais baixa que era na S19: aqui ela é
   argumento de abertura, não a imagem da cena. */
/* #22304A é o cinza da fatia de ensino, sem token próprio; é uma quarta laje
   da mesma família de --med-slab. */
#S19B { --med-slab-3: #22304A }
/* Título + barra viajam juntos. Na batida 0 o par desce ao centro do palco e
   a cena existe sozinha; na batida 1 sobe ao topo, o título colapsa
   (1fr → 0fr, com min-height:0 no filho) e o espaço vai para as colunas. */
#S19B .intensivista {
  display: grid; grid-template-rows: 1fr auto; gap: var(--sp-4);
  flex: 0 0 auto; justify-items: center;
  transition: transform var(--dur-base) var(--ease-out-expo),
              grid-template-rows var(--dur-base) var(--ease-out-expo),
              gap var(--dur-base);
}
#S19B .intensivista__tit { overflow: hidden; min-height: 0 }
#S19B[data-beat="0"] .intensivista { transform: translateY(21rem) }
#S19B[data-beat="1"] .intensivista,
#S19B[data-beat="2"] .intensivista { grid-template-rows: 0fr auto; gap: 0 }
#S19B .timebar {
  /* 13/08: 70rem → 90rem (a largura da blindline da S19, a barra-irmã) quando
     as quatro fatias ganharam rótulo por determinação do palestrante. Em 70rem
     a fatia de 8,9% media ~100px e o overflow:hidden cortaria "9% ensino" em
     silêncio — o mesmo defeito do "Wong et al., JAM…" que a sonda não pega.
     Em 90rem ela mede ~128px e o rótulo quebra em duas linhas (5rem de altura
     comportam 2 × 1,2 × 32px = 77px). */
  width: 90rem; height: 5rem; border-radius: var(--r-md);
  overflow: hidden; display: flex; margin: 0 auto;
}
.timebar__seg {
  flex: 0 0 var(--w); display: grid; place-items: center;
  font: 600 var(--fs-body)/1.2 var(--font-body); color: var(--txt-high);
  text-align: center; padding: 0 var(--sp-1);
}
.timebar__seg[data-seg="doc"]     { background: var(--med-slab) }
.timebar__seg[data-seg="tela"]    { background: var(--med-slab-2) }
.timebar__seg[data-seg="edu"]     { background: var(--med-slab-3) }
.timebar__seg[data-seg="cuidado"] { background: var(--accent) }

/* Batidas da S19B: 0 = só a barra (a justificativa); 1 = entram F2/F3;
   2 = a tarja âmbar. A regra global de .band-warn dispara na batida 1 e aqui
   precisa esperar a 2 — o par de regras abaixo a neutraliza e a reativa. */
#S19B .duo2 {
  height: auto; flex: 1 1 auto; min-height: 0;
  /* O rodapé e a tarja âmbar vivem no fundo da cena: sem esta reserva, os
     números Kaiser/MGB descem por cima deles.
     12/08, à noite: era 11rem, quando o fundo da cena era uma citação escrita
     de 4 linhas. Agora são três blocos empilhados — faixa de cards (168px),
     nota metodológica (~76px) e tarja âmbar (59px) —, e a reserva subiu para
     20rem = 320px. A conta inteira está no comentário de #S19B .band-warn. */
  padding-bottom: 20rem;
  opacity: 0; transform: translateY(var(--sp-4));
  transition: opacity var(--dur-base), transform var(--dur-base) var(--ease-out-expo);
}
#S19B[data-beat="1"] .duo2, #S19B[data-beat="2"] .duo2 { opacity: 1; transform: none }
#S19B[data-beat="1"] .band-warn { opacity: 0; transform: translateY(100%) }
#S19B[data-beat="2"] .band-warn { opacity: 1; transform: none }
/* A tarja âmbar da batida 2 subia colada na base (bottom:0 da regra global) e
   cobria a citação com âmbar opaco — prova de fonte ilegível, que a regra 7 de
   §5.3 não admite. Ela assenta ACIMA de tudo o que vive no fundo da cena.

   12/08, à noite — a conta foi refeita quando a citação escrita virou faixa de
   cards clicáveis com nota metodológica. Empilhando da base para cima:
     faixa de cards   24px (--faixa-b) + 144px (--faixa-h)   →  topo em 168
     nota (2 linhas)  8px + 2 × 1,3 × 26px                   →  topo em 246
     tarja âmbar      assenta em 16,5rem = 264px, mede 59px  →  264 … 323
   Medido na sonda em 12/08 (y de palco, 1080 de altura): os cartões de
   evidência terminam em 685, a tarja ocupa 757..816 e a nota começa em 834 —
   72px de respiro em cima, 18px embaixo. Em 15,5rem sobravam 5px sobre a
   nota, o que a doze metros de distância lê como tarja colada.
   A tarja continua em --fs-lg (36px, acima do piso de 32 de §5.1) com --sp-1
   de padding: com a fonte da regra global (--fs-h2 + --sp-3) ela mede 101px e
   não caberia. Se a nota ganhar ou perder linha, refazer a conta — e conferir
   com 07-scripts/verifica-rodape.mjs, que é quem enxerga a colisão. */
#S19B .band-warn {
  bottom: 16.5rem;
  padding: var(--sp-1) var(--margin-h);
  font-size: var(--fs-lg);
}

/* A S29 empilha seis blocos de texto no alto — a tarja do ato, o selo da SSC
   2026 em duas linhas, e os rótulos das duas colunas — e depois abria um vazio
   de ~200px antes dos cartões, porque as colunas distribuíam a altura em vez de
   assentar no topo. Três medidas resolvem, e nenhuma mexe em tipografia:
   as colunas assentam (`align-content: start`), o recuo do topo passa a ser
   medido a partir do fim do selo, e a base sobe para dar as duas linhas que a
   referência do SCREEN precisa — sem isso ela atravessa a borda dos cartões. */
/* 13/08: o recuo do topo era medido "a partir do fim do selo" da SSC, que vivia
   absoluto no canto direito. O selo virou cartão da primeira coluna e o canto
   ficou livre — o recuo voltou a ser o da margem do palco mais um respiro. */
#S29 .duo2 {
  padding-top: calc(var(--margin-v) + var(--sp-2));
  padding-bottom: var(--sp-6);
  align-items: center;          /* o par assenta no meio do que sobra */
}
#S29 .duo2 > .u-stack { align-content: start; height: auto }
#S29 .scene__ref { bottom: var(--sp-3) }
/* Os dois cartões nascem com a altura do conteúdo e ficavam encolhidos no topo
   de uma tela de 1080. Um piso de altura os põe na escala da cena sem inventar
   conteúdo, e o `space-between` distribui as linhas por dentro. */
#S29 .guide-card, #S29 .evidence { min-height: 26rem }
/* O cartão da diretriz: a etiqueta em --ok (verde de "recomendado") contra o
   cartão âmbar da coluna vizinha. As duas cores lado a lado SÃO o argumento da
   cena — o que a diretriz deste ano endossa, e o que ninguém provou. */
#S29 .guide-card__tag { color: var(--ok) }
#S29 .guide-card { border-color: var(--ok) }
#S29 .guide-card strong { color: var(--ok); font-weight: 700 }
#S29 .u-stack > .card-glass { display: grid; align-content: space-between }
/* --- Os dois cartões de evidência da S19B --------------------------------- */
/* Não usam `.evidence` (que é grid de três colunas e serve a outra cena): aqui
   cada cartão é uma pilha de três linhas — número, o que ele é, e a base
   amostral. O piso de altura os põe na escala da cena; sem ele, dois cartões
   de três linhas ficam encolhidos no meio de um palco de 1080. */
.evid-card { min-height: 20rem; align-content: center; justify-items: start }
.evid-card .evidence__num { font-size: var(--fs-d2) }

/* --- S27 · As ferramentas que devolvem tempo (F2, F3, F4) · 0:45 -------- */
/* Cena nova de 12/08. Três colunas iguais: em 1920 cada uma mede 544px, que é
   o que o mock do F2 (26rem) e o formulário da F4 pedem, e sobra de folga para
   o celular da F3, que é estreito por natureza.

   --mock-h ERA 30rem, a régua herdada da S19B antes da separação, quando cada
   coluna ainda carregava uma linha de evidência embaixo. Às 23h de 12/08 saíram
   as três palavras "nada" e a nota metodológica, e sobraram 120px de vão morto
   entre o selo da F3 e a faixa de citação — numa cena cujo argumento inteiro é
   VER a ferramenta funcionando. O vão foi para a gravação: 34rem.

   A conta, em y de palco (1080 de altura), com a régua nova:
     act-tag termina           117
     rótulo + respiro          169
     celular 545 (34rem)       714
     respiro + selo            780   ← só a coluna da F3 tem estes dois
     faixa de citação começa   909   (--provas-band = 176px de reserva)
   Medido na sonda: 129px de respiro sob a coluna mais alta. Cada rem a mais
   come 16 desses — em 36rem sobrariam 97, ainda cabe; em 30rem sobravam 193,
   que é o buraco que motivou a mudança. Quem mexer mede de novo: a sonda de
   geometria está descrita no REGISTRO de 12/08, §11. */
#S27 { --mock-h: 34rem }
#S27 .trio-tools {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-6);
  align-items: stretch; justify-items: center;
  padding-top: var(--sp-5);
  /* 12/08, à noite: a cena trocou a legenda âmbar em fluxo por uma faixa de
     cards de citação, que é ABSOLUTA. Absoluto não empurra ninguém — quem
     reserva o espaço tem de ser este padding, ou as três colunas descem por
     cima da prova de fonte.
     Era --provas-band-nota (216px) enquanto havia nota metodológica em tela;
     às 23h a nota saiu e a reserva voltou para a faixa pura (176px). */
  padding-bottom: var(--provas-band);
}
/* Centra as PEÇAS na coluna, não o texto DENTRO delas: com `text-align:center`
   herdado, a bolha do F2 virava três linhas centralizadas — que não é como
   mensagem de WhatsApp se parece — e as alíneas do formulário da F4 saíam do
   eixo do a)/b). Quem centraliza é o eixo transversal da coluna; o texto de
   cada peça mantém o alinhamento que a peça já tinha na cena de origem.

   COLUNA EM FLEX, e não no grid que o .u-stack dá. Nasceu às 22h de 12/08 para
   alinhar as três linhas de evidência (a F3 tem um elemento a mais — o selo
   "sugestão — revisar e assinar" — e empurrava a sua 60px para baixo). Às 23h
   as linhas saíram de cena e o alinhamento perdeu o objeto; a coluna continua
   em flex porque `justify-items` do grid não centra o selo da F3, que é mais
   largo que o celular, e trocar de volta reabriria um caso já medido. */
#S27 .tool {
  display: flex; flex-direction: column; gap: var(--sp-3);
  align-items: center; height: 100%;
}
#S27 .t-label { text-align: center }
/* `#S27 .chat-mock` e `#S27 .do-form` saíram em 12/08 (segunda rodada): as
   três colunas passaram a ser celulares de verdade, com as três gravações. A
   cena ficou simétrica — mesma moldura, mesma régua, mesma razão 384×848 — e
   o que distingue as ferramentas voltou a ser o que está DENTRO da tela, que é
   o argumento do slide. O `.do-form` continua vivo na S29, onde é a tese. */

/* A LINHA DE EVIDÊNCIA DE CADA FERRAMENTA NÃO EXISTE MAIS, e o rastro fica
   porque quatro regras morreram com ela em 12/08, todas por determinação do
   palestrante, em duas rodadas:
     22h  `#S27 .tool__ev.u-between` e `#S27 .tool__ev .t-mono` — vestiam os
          dois números do Kaiser e do Mass General embaixo da F3. Os números
          saíram: eles moram na S19B, um slide antes, com denominador e
          ressalva. `#S27 .tools__legenda` — a legenda âmbar "medido em outro
          serviço, outra ferramenta" — saiu junto, porque sem número em cena
          ela não qualificava nada.
     23h  `#S27 .tool__ev` e `#S27 .tool__ev--zero` — vestiam a palavra
          "nada" sob cada uma das três, e a .provas__nota que a explicava.
          Saíram também.
   A cena virou vitrine limpa: rótulo, gravação, e os dois cards no rodapé.
   Nenhuma dessas classes é usada em outra cena — se voltarem, voltam com o
   HTML delas. O placar do que ninguém provou passou a ser inteiramente FALADO
   nesta cena; escrito, ele só existe na S29. */
/* `.evo__gen` (a barra lateral do trecho gerado) foi removida em 12/08: era da
   maquete tipográfica que o vídeo substituiu, e nenhum HTML do deck a usava
   mais. Quem marca o gerado agora é a própria tela do app, dentro do vídeo, e
   o selo abaixo. */
.evo__flag {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  border: 1px solid var(--ai-edge); border-radius: var(--r-full);
  padding: var(--sp-1) var(--sp-2); color: var(--ai-glow);
  font: 600 var(--fs-label)/1 var(--font-body);
  letter-spacing: var(--tr-label); text-transform: uppercase;
}
/* --- O celular do F3 (pendência 7, fechada em 12/08) ----------------------
   No lugar do print estático entrou a gravação de tela do app, em loop. A
   altura vem de --mock-h, a MESMA do .chat-mock do F2: as duas ferramentas do
   ato aparecem na mesma régua, e o par não fica torto. A largura sai da razão
   nativa do arquivo — 384×848 — e não de um palpite: um celular com proporção
   inventada é a primeira coisa que denuncia mockup.

   Nada aqui reescala o vídeo para cima. Em 1920×1080 esta caixa mede 480px de
   altura contra os 848px do arquivo, ou seja o material é REDUZIDO na
   projeção. É por isso que o texto do app continua limpo no telão sem passar
   por upscaler nenhum — e é a razão de o vídeo ter sido MONTADO das gravações
   reais, e não gerado nem ampliado por IA: upscaler generativo redesenha
   glifo pequeno, e aqui glifo pequeno é dose de noradrenalina. */
/* 12/08, segunda volta: o par lado-a-lado (.evo-duo/.evo-lado) morreu quando as
   ferramentas saíram da S19B para a S27. Lá a coluna tem 544px e não 832: não
   há largura ociosa para pôr nada ao lado do celular, e a pilha volta a ser o
   arranjo certo. As duas classes saíram do CSS junto com o HTML que as usava —
   regra órfã em folha de 2.000 linhas é armadilha para quem vier depois. */
.evo-phone { --phone-h: var(--mock-h, 26rem); margin: 0 }
.evo-phone__btn {
  position: relative; display: block; padding: 0; cursor: pointer;
  width: calc(var(--phone-h) * 384 / 848); height: var(--phone-h);
  border: 3px solid var(--med-border); border-radius: var(--r-xl);
  background: #F2F2F7;      /* o cinza do próprio app: nenhum flash branco
                               entre o poster e o primeiro frame */
  box-shadow: var(--elev-3), var(--glow-ai);
  transition: transform var(--dur-fast) var(--ease-out-expo);
}
.evo-phone__btn:hover  { transform: translateY(-2px) }
.evo-phone__btn:focus-visible {
  outline: 2px solid var(--ai-glow); outline-offset: 4px;
}
.evo-phone__video {
  display: block; width: 100%; height: 100%;
  border-radius: calc(var(--r-xl) - 3px);
  object-fit: cover;        /* a razão da caixa é a do arquivo: não corta nada */
}
/* A affordance de clique é ÍCONE, sem rótulo. Com o texto "ver evolução" ela
   nascia em --fs-label (24px, o piso do deck) dentro de uma moldura de 217px:
   quebrava em duas linhas e tapava justamente a tela do app que o slide existe
   para mostrar. Diminuir a fonte para caber seria furar o piso de legibilidade
   por causa de um enfeite. Quem clica aqui é o palestrante, que sabe onde
   clicar; para a plateia, o que importa é a tela por baixo. */
.evo-phone__lupa {
  position: absolute; right: var(--sp-1); bottom: var(--sp-1);
  display: grid; place-items: center;
  width: var(--sp-4); height: var(--sp-4); border-radius: var(--r-full);
  border: 1px solid var(--ai-edge);
  background: rgba(5, 11, 24, .82); color: var(--ai-glow);
  backdrop-filter: blur(6px);
}
.evo-phone__lupa svg { width: var(--sp-2); height: var(--sp-2) }
/* O selo de P6 fica FORA da moldura, embaixo: dentro dela taparia a tela do app
   justamente onde o texto gerado aparece, e é esse texto que ele qualifica.
   letter-spacing menor SÓ aqui: com os .12em do padrão a frase não cabe numa
   linha da coluna de 544px e quebra dentro da pílula. O glifo continua em 24px,
   o piso do deck — quem encolhe é o espaço entre as letras, não a letra. */
#S27 .tool > .evo__flag {
  margin: 0; width: max-content; max-width: 100%;
  letter-spacing: .04em;
}

/* --- A evolução inteira, ao clique ---------------------------------------
   Mora no <body>, fora de .stage: aqui `rem` é o do documento (16px) e não o
   do palco, igual ao .toast. Por isso as medidas abaixo são em vw/vh e rem de
   documento — dentro do palco elas mudariam de tamanho junto com o letterbox.

   z-index 950: acima de tudo que o palco pinta, abaixo do .blackout (999).
   Apertar `b` durante a fala tem de apagar a tela mesmo com o overlay aberto. */
.evo-doc {
  position: fixed; inset: 0; z-index: 950;
  display: grid; place-items: center; padding: 3vh 3vw;
  background: rgba(5, 11, 24, .92); backdrop-filter: blur(10px);
  opacity: 0; pointer-events: none;
  transition: opacity var(--dur-fast) var(--ease-out-expo);
}
.evo-doc[data-on="1"] { opacity: 1; pointer-events: auto }
.evo-doc__painel {
  display: grid; grid-template-rows: auto 1fr; gap: 0;
  width: min(78rem, 94vw); max-height: 94vh; overflow: hidden;
  border: 1px solid var(--stroke-hi); border-radius: 1rem;
  background: var(--bg-800); box-shadow: var(--elev-3);
}
.evo-doc__topo {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 1rem 1.25rem; border-bottom: 1px solid var(--stroke);
  background: var(--bg-700);
}
.evo-doc__titulo {
  margin: 0; color: var(--txt-high);
  font: 700 1.0625rem/1.2 var(--font-display);
}
.evo-doc__selo {
  display: inline-flex; align-items: center; gap: .375rem; margin: .25rem 0 0;
  color: var(--ai-glow); font: 600 .75rem/1 var(--font-body);
  letter-spacing: .08em; text-transform: uppercase;
}
.evo-doc__fechar {
  flex: none; width: 2.25rem; height: 2.25rem; display: grid; place-items: center;
  border: 1px solid var(--stroke-hi); border-radius: 999px;
  background: var(--bg-800); color: var(--txt-mid); cursor: pointer;
  font: 400 1.25rem/1 var(--font-body);
}
.evo-doc__fechar:hover { color: var(--txt-high); border-color: var(--ai-edge) }
.evo-doc__rolo { overflow-y: auto; padding: 1.25rem 1.5rem 2rem }
.evo-doc__origem {
  margin: 0 0 1rem; padding: .625rem .875rem;
  border-left: 3px solid var(--ai-core); border-radius: 0 .375rem .375rem 0;
  background: rgba(255,255,255,.03); color: var(--txt-mid);
  font: 500 .8125rem/1.5 var(--font-body);
}
/* <pre> e não <p>: a evolução do app é texto monoespaçado com quebra e recuo
   próprios, e é assim que ela aparece na tela que a plateia acabou de ver.
   Reflowar em parágrafo faria o overlay contradizer o vídeo. */
.evo-doc__corpo {
  margin: 0; white-space: pre-wrap; overflow-wrap: anywhere;
  color: var(--txt-high); font: 400 1.125rem/1.6 var(--font-mono);
}

/* --- As transcrições de conversa (F2 e F4) ------------------------------- */
/* O overlay do F3 é um documento (a evolução), e por isso é <pre> corrido. Os
   do F2 e do F4 são CONVERSAS, e conversa lida em bloco corrido perde o que
   ela tem de mais importante aqui: quem disse o quê. Duas colunas de bolha
   resolvem isso sem imitar o WhatsApp — imitar seria pastiche, e a plateia
   acabou de ver o WhatsApp de verdade no celular ao lado.
   `pre-wrap` porque as falas do agente têm quebra e lista dentro. */
.fala {
  white-space: pre-wrap; overflow-wrap: anywhere;
  max-width: 82%; margin: 0 0 1rem;
  padding: .75rem 1rem; border-radius: .75rem;
  font: 400 1.0625rem/1.55 var(--font-body);
}
.fala b { font-weight: 700 }
/* O rótulo do autor é bloco e some da leitura corrida: quem lê no telão vê
   "Fernando"/"Agente" como etiqueta, não como primeira palavra da frase. */
.fala > b:first-child {
  display: block; margin-bottom: .375rem;
  font: 700 .75rem/1 var(--font-body);
  letter-spacing: .1em; text-transform: uppercase; opacity: .75;
}
.fala--eu {
  margin-left: auto; background: rgba(45, 212, 191, .12);
  border: 1px solid var(--ai-edge); color: var(--txt-high);
}
.fala--bot {
  background: var(--bg-700); border: 1px solid var(--stroke);
  color: var(--txt-high);
}
.fala--bot i { color: var(--ai-glow); font-style: normal }
@media print { .evo-doc { display: none } }

/* --- O celular no modo de leitura vertical (≤900px) -----------------------
   No palco a coluna do F3 tem 832px de largura e o par celular↔texto cabe de
   lado. Num celular de 390px não cabe: a moldura vira uma tira de 60px e o
   selo quebra em quatro linhas. Aqui o par empilha, e a altura passa a ser
   medida pela LARGURA disponível — `--phone-h` sai de 30rem para uma conta com
   a razão nativa 848/384, para a moldura não passar de meia tela.

   O overlay não precisa de nada: ele já mede em vw/vh. */
@media (max-width: 900px) {
  #S27 .trio-tools { display: grid; grid-template-columns: 1fr; gap: 2rem; justify-items: start }
  /* `align-items`, e não `justify-items`: a coluna virou flex em 12/08 às 22h
     e o `justify-items: start` que estava aqui deixou de ter efeito naquele
     instante — as três peças continuavam centradas no modo de leitura, onde
     tudo o mais é alinhado à esquerda. Nenhum verificador acusa: não estoura
     largura nem cobre citação, só fica torto. */
  #S27 .tool { align-items: flex-start; text-align: left }

  .evo-phone { --phone-h: calc(min(62vw, 16rem) * 848 / 384) }
  #S27 .tool > .evo__flag { white-space: normal }
  .evo-doc__corpo { font-size: .9375rem }
}

/* --- S28 · Medicina personalizada · 0:20 · data-cut="2" ----------------- */
/* --- S28 · a pilha para na faixa do rodapé ------------------------------ */
/* Era style inline com height:100%: a pilha ia até y=1008 e o grid ESTICAVA a
   última linha até lá — a tarja de --warn nascia com 111,6px de conteúdo e era
   inflada para 148,6, entrando 27,6px nas duas linhas da citação, em toda a
   largura. Agora a caixa termina em --ref-band e align-content:start manda a
   folga para o pé, que é exatamente onde o .chip-callback--corner mora.
   padding-top desce de --sp-6 para --sp-5: são os 16px que pagam a faixa sem
   encostar no .act-tag (que acaba em y=92,7; as barras passam a começar em
   y=116,7, folga de 24). A altura das barras (31,25rem) NÃO muda — as frações
   5/13/24/40 da S28 são calculadas sobre ela. */
#S28 > .u-stack {
  /* gap de 24px e não 48: a faixa de provas com nota comeu 96px de altura
     útil, e era exatamente dessa folga que o .chip-callback--corner vivia —
     sem ela, a tarja âmbar descia até a base do stack e o chip do callback
     imprimia por cima dela. Os três gaps devolvem 72px, e a folga volta
     para o pé, que é onde o chip mora. */
  gap: var(--sp-3); padding-top: var(--sp-3);
  height: calc(100% - (var(--provas-band-nota) - var(--margin-v)));
  align-content: start;
}
/* Na S28 o chip sai do absoluto e vira o ÚLTIMO ITEM DO STACK, alinhado à
   direita. Absoluto ele dependia de uma folga no pé que a faixa de provas
   com nota consumiu — e passou a imprimir sobre a tarja âmbar, justamente a
   linha que a PARTE 8 exige que seja lida inteira (benefício → dano). Em
   fluxo, ele não pode colidir com nada: o grid o empurra. */
#S28 .chip-callback--corner {
  position: static; justify-self: end; bottom: auto; right: auto;
  margin-top: var(--sp-1);
}
/* A altura das barras é VARIÁVEL da cena porque as quatro frações
   (5/13/24/40) são calculadas sobre ela no HTML. Caiu de 31,25rem para 27rem
   em 12/08: a faixa de provas com nota tirou 96px de altura útil e o stack
   passou a transbordar — o chip do callback imprimia sobre a nota do Monte
   Carlo. Quem mexer aqui não precisa mexer no HTML: ele lê --bars-h. */
#S28 { --bars-h: 27rem }
#S28 .bars { display: flex; align-items: flex-end; gap: var(--sp-5); height: var(--bars-h) }
.bars__col {
  width: 11rem; height: var(--h); background: var(--c, var(--chart-1));
  border-radius: var(--r-sm) var(--r-sm) 0 0;
  display: grid; align-content: end; justify-items: center; gap: var(--sp-1);
  padding-bottom: var(--sp-2); color: var(--txt-high);
  font: 700 var(--fs-h2)/1 var(--font-display);
}
.bars__name { font: 500 var(--fs-body)/1 var(--font-mono) }

/* --- S29B · A regra: CFM, LGPD e ANVISA · 0:35 · data-cut="2" ---------- */
/* Cena nova de 13/08. Reaproveita a grade da S29 — duas colunas, cartões que
   assentam no topo e um piso de altura para não ficarem encolhidos numa tela
   de 1080. A diferença é o que cada coluna carrega: aqui a esquerda é a norma
   brasileira e a direita são as duas que quase ninguém acerta. */
/* TABELA DE NORMAS (13/08, segunda rodada). Três linhas, duas colunas: quem
   obriga, e o que muda. A coluna da esquerda tem largura FIXA — é o que faz as
   três normas se alinharem numa régua vertical e o olho ler "CFM, LGPD,
   ANVISA" de cima a baixo antes de ler qualquer frase. Com `auto` cada rótulo
   media o seu texto e a régua sumia. */
#S29B .normas {
  display: grid; gap: var(--sp-5);
  height: 100%; align-content: center;
  padding-top: calc(var(--margin-v) + var(--sp-5));
  /* A faixa de citação e a nota são absolutas: quem reserva o espaço delas é
     este padding, ou a última norma desce por cima da prova de fonte. */
  padding-bottom: var(--provas-band-nota);
}
#S29B .norma {
  display: grid; grid-template-columns: 20rem 1fr; gap: var(--sp-6);
  align-items: start;
  padding-bottom: var(--sp-5);
  border-bottom: 1px solid var(--stroke);
}
#S29B .norma:last-child { border-bottom: 0; padding-bottom: 0 }
#S29B .norma__tag { display: grid; gap: var(--sp-1); align-content: start }
/* A data em verde é o único ponto de cor da cena, e é ela que a plateia vai
   fotografar: é o que muda a vida deles daqui a onze dias. */
#S29B .norma__vig { color: var(--ok) }
#S29B .norma__txt { display: grid; gap: var(--sp-2) }
/* A citação literal do Art. 15 é o único texto em 44px da cena. As linhas das
   outras duas normas ficam em --fs-lg para não competir com ela, e as duas
   consequências do CFM em --fs-body, que é o piso de §5.1. */
#S29B .norma__txt .t-h2 { margin-bottom: var(--sp-2) }

/* --- S29 · Perspectivas futuras + o que ninguém provou · 0:35 ----------- */
/* O que sobrou destas regras NÃO é mais da S29: em 13/08 o formulário da
   declaração de óbito saiu de cena e a diretriz da SSC tomou o lugar dele.
   `.do-form` e `.do-form__row` continuam vivos em ato-1.html, onde desenham o
   papel pautado dos cards flutuantes da tela partida — inclusive o contador de
   alíneas, que ali é o que faz o card parecer um impresso. */
.do-form {
  display: grid; gap: var(--sp-1); font: 500 var(--fs-body)/1.3 var(--font-mono);
  counter-reset: do-linha;
}
.do-form__row { display: grid; grid-template-columns: 2rem 1fr; gap: var(--sp-2); color: var(--med-ink) }
/* A alínea a) b) c) é ordinal de formulário, não texto de leitura: nasce de
   contador para não gastar o orçamento de palavras da cena. O ")" é o único
   literal aqui, e é pontuação do impresso da DO. */
.do-form__row::before { counter-increment: do-linha; content: counter(do-linha, lower-alpha) ")" }
/* `.do-form__row--rejected` SAIU EM 13/08 com a linha que ela vestia — a
   "parada cardiorrespiratória — bloqueado", tracejada em vermelho, que era o
   único elemento em --accent da S29. Se a linha voltar, a regra volta com ela;
   guardar CSS sem HTML é o jeito mais barato de acumular mentira. */

/* O SELO DE DIRETRIZ (`.guide-seal` e as quatro classes filhas) SAIU EM
   13/08/2026. A Recomendação 2 da SSC 2026 morava aqui, absoluta no canto
   superior direito, em corpo de rodapé — e era a coisa mais forte da S29 com o
   menor corpo dela. Ela graduou para cartão da primeira coluna, em 44px
   (`#S29 .guide-card`, acima), no lugar do formulário da declaração de óbito.
   Nenhuma outra cena usava estas classes. */

/* ============================================================================
   A DEMO AO VIVO — hoje o fecho do ATO IV (S24–S26)
   Um só renderizador, três fontes (§6.1). O painel direito é idêntico em
   LIVE, SIM e vídeo — é isso que faz a plateia não distinguir os modos.
   ========================================================================== */

/* --- S24 · Setup · 0:30 ------------------------------------------------- */
#S24 .lights { display: flex; gap: var(--sp-4); justify-content: center }
.light {
  height: 3.5rem; display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: 0 var(--sp-3); border-radius: var(--r-full);
  border: 1px solid var(--stroke); background: var(--bg-glass);
  font: 500 var(--fs-body)/1 var(--font-body); color: var(--txt-mid);
}
.light__dot { width: 1rem; height: 1rem; border-radius: var(--r-full); background: var(--txt-dim) }
.light[data-status="ok"]   .light__dot { background: var(--ok);     box-shadow: 0 0 12px var(--ok) }
.light[data-status="warn"] .light__dot { background: var(--warn);   box-shadow: 0 0 12px var(--warn) }
.light[data-status="crit"] .light__dot { background: var(--accent); box-shadow: 0 0 12px var(--accent) }
.light[data-status="check"] .light__dot { animation: pulse 1s ease-in-out infinite }
/* Semáforo vermelho também muda o rótulo: um ponto de 16px não se lê do fundo
   da sala, e no pre-flight (§6.3) é o rótulo que diz o que consertar. */
.light[data-status="crit"] { border-color: var(--accent); color: var(--accent-soft) }
.light[data-status="warn"] { color: var(--med-stamp) }

/* --- S25 · O alerta · 2:00 · NUNCA CORTAR ------------------------------- */
#S25 .demo { display: grid; grid-template-columns: 60fr 40fr; gap: var(--gutter); height: 100% }

/* Espelho da janela real, no modo LIVE. */
.mirror {
  width: 100%; height: 100%; object-fit: cover; display: block;
  border-radius: var(--r-xl); background: var(--bg-900);
}

/* O canal de mensagens em modo SIM (§6.5). Interface na linguagem do próprio
   deck: não se recria o WhatsApp pixel a pixel — é mais bonito assim e evita
   reproduzir trade dress de terceiro num deck que fica público. */
.sim {
  display: grid; grid-template-rows: auto 1fr auto; height: 100%;
  background: var(--med-bg); border: 1px solid var(--med-border);
  border-radius: var(--r-xl); overflow: hidden;
}
.sim__head {
  padding: var(--sp-3) var(--sp-4); background: var(--bg-700);
  font: 600 var(--fs-label)/1.2 var(--font-body);
  letter-spacing: var(--tr-label); text-transform: uppercase; color: var(--txt-mid);
  display: flex; justify-content: space-between; align-items: center; gap: var(--sp-3);
}
/* O selo é de base.css e é permanente (ADR-0011) — aqui ele só é ancorado:
   flex:none para nunca encolher, e a barra nunca o deixa quebrar linha. */
.sim__head .sim-badge { flex: none }
.sim__head > span:first-child { min-width: 0 }
/* O contador "+N na fila" da interação com a plateia (§6.7). Nasce escondido
   por [hidden] no HTML; quem o revela é js/demo.js. */
.sim__head [data-demo-queue] {
  flex: none; color: var(--med-stamp);
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  text-transform: none; letter-spacing: 0;
}

/* justify-content: flex-end empilha as mensagens a partir da base, como um
   canal real. min-height:0 é o que impede a trilha 1fr de crescer com o
   conteúdo e estourar o painel: sem ele, o sexto balão empurra o rodapé para
   fora da cena antes de a poda de js/demo.js acontecer.
   A máscara no topo é o que separa "mensagem antiga saindo" de "balão cortado
   ao meio": o excedente se dissolve em vez de bater numa guilhotina. Máscara
   é composição na GPU, não repaint — cabe no orçamento de 8 ms. */
.sim__feed {
  padding: var(--sp-4); display: flex; flex-direction: column;
  gap: var(--sp-3); justify-content: flex-end; overflow: hidden; min-height: 0;
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 var(--sp-7));
          mask-image: linear-gradient(180deg, transparent 0, #000 var(--sp-7));
}
.bubble {
  flex: none;
  max-width: 78%; padding: var(--sp-3) var(--sp-4); border-radius: var(--r-lg);
  font: 500 var(--fs-body)/var(--lh-body) var(--font-body); white-space: pre-line;
  animation: bubbleIn .34s var(--ease-overshoot) both;
}
.bubble--agente {
  align-self: flex-start; background: var(--med-surface);
  color: var(--med-ink); border: 1px solid var(--med-border);
}
/* --primary é o mesmo azul escuro nos dois temas, então o texto do balão do
   médico precisa ser claro nos dois. --txt-high inverte com o tema e ficaria
   navy sobre navy no modo claro de emergência; --bg-900 é claro justamente lá. */
.bubble--medico { align-self: flex-end; background: var(--primary); color: var(--txt-high) }
:root[data-theme="light"] .bubble--medico { color: var(--bg-900) }
.bubble--alerta { border-color: var(--accent); box-shadow: var(--glow-alert) }
/* O escalonamento pulsa por filtro, e não por box-shadow animado: sombra
   animada repinta a caixa inteira a cada frame e o orçamento é de 8 ms. */
.bubble--escalate {
  /* Literal local: --accent a 12% de opacidade. tokens.css não tem versão
     translúcida do accent, e criar um token para um único uso polui a paleta. */
  --escalate-wash: rgba(230, 57, 70, .12);
  border-color: var(--accent); background: var(--escalate-wash);
  animation: bubbleIn .34s both, pulseCrit 1.4s .4s 2;
}
/* Pergunta da plateia promovida da fila (§6.7). js/demo.js dá a ela as classes
   de .bubble--medico, porque o remetente é humano; o rótulo existe para a
   plateia saber que aquela pergunta é dela, e não do roteiro. */
.bubble--plateia { border: 1px solid var(--ai-edge) }
.bubble--plateia::before {
  content: 'Da plateia'; display: block; margin-bottom: var(--sp-1);
  font: 700 var(--fs-label)/1 var(--font-body);
  letter-spacing: var(--tr-label); text-transform: uppercase;
  color: inherit; opacity: .72;   /* herdar é o que o mantém legível nos 2 temas */
}
/* Saída do balão podado: o feed tem 5 e os antigos sobem e somem, sem barra de
   rolagem (regra 17 de §5.3). js/demo.js marca data-out e remove em 400 ms. */
.bubble[data-out="1"] {
  animation: bubbleOut .4s var(--ease-in-out) both;
}
/* Reconstrução ao voltar uma batida: cinco balões reencenando a entrada de uma
   vez seria exatamente o que o CONTRATO §2 proíbe. */
.bubble[data-instant] { animation: none }
.bubble__cite {
  display: block; margin-top: var(--sp-2); padding-top: var(--sp-2);
  border-top: 1px solid var(--stroke);
  font: 400 var(--fs-ref)/1.3 var(--font-mono); color: var(--txt-low);
}
.bubble__time { display: block; margin-top: .25rem; font: 400 var(--fs-ref)/1 var(--font-mono); color: var(--txt-dim) }
/* Sobre o azul do balão do médico, cita e hora herdam a cor do balão e recuam
   por opacidade — token de texto fixo inverteria junto com o tema e sumiria. */
.bubble--medico .bubble__cite { color: inherit; border-top-color: currentColor; opacity: .78 }
.bubble--medico .bubble__time { color: inherit; opacity: .7 }
@keyframes bubbleIn { from { opacity: 0; transform: translateY(1.125rem) scale(.97) } }
@keyframes bubbleOut { to { opacity: 0; transform: translateY(-1.5rem) scale(.96) } }
@keyframes pulseCrit { 50% { filter: drop-shadow(0 0 2.5rem var(--accent)) } }
/* Digitação simulada: três pontos antes de cada balão do agente. Custa nada
   e é o que faz a simulação parecer viva. */
.typing { display: inline-flex; gap: .375rem; align-self: flex-start; padding: var(--sp-3) var(--sp-4) }
.typing i {
  width: .625rem; height: .625rem; border-radius: var(--r-full);
  background: var(--txt-low); animation: typing 1.2s ease-in-out infinite;
}
/* O atraso é por posição e não por --i: js/demo.js cria três <i> nus, e sem
   isto os três pontos pulsariam em uníssono — que não parece digitação. */
.typing i:nth-child(2) { animation-delay: .16s }
.typing i:nth-child(3) { animation-delay: .32s }
@keyframes typing { 50% { opacity: .25; transform: translateY(-.25rem) } }

/* O painel do protocolo — o mesmo da S18, reduzido, dirigido pelo event bus.
   A borda conta o estado sem uma palavra a mais: enquanto é suspeita ela é a
   do alerta; depois que o humano abriu o protocolo, é a do processo em curso.
   position: relative existe para a marca transitória de roteamento. */
.panel {
  position: relative;
  display: grid; grid-template-rows: auto auto minmax(0, 1fr) auto; gap: var(--sp-2);
  height: 100%; min-height: 0; padding: var(--sp-2);
  background: var(--bg-800); border: 1px solid var(--stroke);
  border-radius: var(--r-xl); box-shadow: var(--elev-1);
  transition: border-color var(--dur-base) var(--ease-out-expo);
}
.panel[data-state="suspeita"] { border-color: var(--accent) }
.panel[data-state="aberto"]   { border-color: var(--ok) }
.panel > * { min-width: 0 }
/* Os dois rótulos do cabeçalho quebram em duas linhas cada nesta largura; sem
   ancorar cada um ao seu lado eles se leem como uma frase só. */
.panel > header { align-items: start; gap: var(--sp-3) }
.panel > header > :last-child { text-align: right }
.panel .u-stack { gap: var(--sp-2); grid-template-rows: auto auto auto minmax(0, 1fr) }
/* p sem classe tipográfica herda a margem do agente de usuário: 1em de 88px
   são 176px de vazio no meio do painel, e é o que o estourava. */
.panel__timer {
  margin: 0;
  font: 700 var(--fs-timer)/1 var(--font-mono); font-variant-numeric: tabular-nums;
  color: var(--med-stamp);
}
.panel__timer[data-state="late"] { color: var(--accent) }
/* Seis etapas numa coluna de 40% do palco (§ storyboard S25: col 8–12). Aqui o
   item vira linha — ícone à esquerda, rótulo ao lado — e as seis linhas
   dividem em partes iguais a altura que sobrar, seja ela qual for. O piso
   tipográfico de 32px do rótulo é que não se negocia para ganhar espaço. */
.panel .bundle {
  grid-auto-flow: row; grid-template-rows: repeat(6, minmax(0, 1fr));
  gap: var(--sp-1); min-height: 0;
}
/* O display: grid de .bundle vence o [hidden] do agente de usuário, e o
   checklist aparecia antes da batida 4 — desmentindo a fala "eu tive que
   digitar abrir protocolo". Sem o módulo carregado, ele nem chega a existir. */
.bundle[hidden] { display: none }
.panel .bundle__item {
  grid-template-columns: 2rem 1fr; gap: var(--sp-2);
  align-items: center; align-content: center;
  padding: 0 var(--sp-2); overflow: hidden;
}
/* As pílulas do gatilho ficam mais baixas no painel do que na S18: são cinco,
   e duas fileiras delas custam altura que o bundle precisa. */
.panel .crit-pill { padding: 0 var(--sp-2); line-height: 1.25 }
.panel .crit-row { gap: var(--sp-1) }
/* O cadeado da decisão humana: fechado até o médico mandar abrir. */
.panel__lock { color: var(--accent); transition: opacity var(--dur-fast) }
.panel[data-state="aberto"] .panel__lock { opacity: .25 }

/* A batida 2 mostra por que cada destinatário recebeu — vínculo, escala,
   severidade. js/demo.js liga data-highlight e o apaga sozinho em 2,4 s; a
   marca é um anel que só anima opacidade, e some sem deixar estado. */
[data-demo-panel][data-highlight="escala"]::after {
  content: ''; position: absolute; inset: calc(var(--sp-1) * -1);
  border: 2px solid var(--ai-edge); border-radius: var(--r-xl);
  pointer-events: none;
  animation: panelMark 2.4s var(--ease-in-out) both;
}
@keyframes panelMark { 0%, 100% { opacity: 0 } 14%, 74% { opacity: 1 } }

/* Gatilho: as pílulas nascem apagadas e acendem uma a uma na batida 1. */
.crit-pill { transition: color var(--dur-fast), border-color var(--dur-fast), background-color var(--dur-fast) }
.crit-pill[data-on="0"] { color: var(--txt-low); border-color: var(--med-border); background: none }
/* Etapa em atenção — o nível intermediário entre pendente e vencida. */
.bundle__item[data-state="warn"] { border-color: var(--warn) }
.bundle__item[data-state="warn"] svg { color: var(--warn) }

/* O aviso de ficção. Vale para a S24 e para qualquer cena do ato: o caso é
   inventado, e isso se diz por escrito, não só de viva-voz. */
.disclaimer { color: var(--warn) }

/* Vídeo de fallback (tecla v). Pré-carregado e escondido — nunca carrega na
   hora, e é mudo por especificação (§6.6). */
.fallback-video {
  position: absolute; inset: 0; z-index: 90; width: 100%; height: 100%;
  object-fit: cover; background: #000; display: none;
}
.fallback-video[data-on="1"] { display: block }

/* --- S26 · O que vocês acabaram de ver · 0:30 · data-cut="3" ------------ */
/* height:auto no .three, e não 100%: a 100% ele consumia a pilha inteira e a
   frase final ("Nada disso é predição…") era empurrada 28px para dentro da
   citação do Arabi. A pilha centra o conjunto e reserva a faixa do rodapé. */
#S26 > .u-stack { align-content: center; padding-bottom: var(--provas-band) }
#S26 .three { display: grid; gap: var(--sp-5); align-content: center; height: auto }
#S26 .three > * { display: grid; grid-template-columns: 6rem 1fr; gap: var(--sp-4); align-items: baseline }
#S26 .three__n { font: 700 var(--fs-timer)/1 var(--font-mono); color: var(--accent) }

/* ============================================================================
   ATO VI — O FECHAMENTO
   ========================================================================== */

/* --- S30 · O recado · 0:30 · NUNCA CORTAR ------------------------------- */
#S30 { background: radial-gradient(60% 50% at 50% 45%, rgba(11,77,166,.22), transparent), var(--bg-900) }
/* HUMANIZADA volta ao centro (FLIP reverso, 700 ms) e as outras três
   reentram menores ao redor. Elas orbitam por transform, com origem no
   centro da palavra grande. */
#S30 .hz { font: 800 var(--fs-d2)/var(--lh-d2) var(--font-display); letter-spacing: var(--tr-d2); color: var(--accent) }
#S30 .orbit { position: relative; display: grid; place-items: center; min-height: 20rem }
/* Órbita por ângulo e raio: a contra-rotação mantém a palavra na horizontal.
   A entrada anima só a opacidade — wordIn zeraria o transform e jogaria as
   três palavras uma em cima da outra no centro. */
#S30 .orbit__w {
  position: absolute; font: 700 var(--fs-h1)/1 var(--font-display);
  color: var(--primary-soft);
  transform: rotate(var(--a, 0deg)) translateX(var(--r, 22rem)) rotate(calc(var(--a, 0deg) * -1));
  opacity: 0; animation: fadeIn .5s var(--ease-out-expo) calc(700ms + var(--i, 0) * 140ms) forwards;
}
@keyframes fadeIn { to { opacity: 1 } }
#S30 .s30__punch { color: var(--accent) }
/* Reforma de 13/08: o slide ganhou a tese em três linhas + assinatura.
   O mito entra menor e em --txt-mid (é o senso comum, não o argumento);
   a assinatura em t-label --txt-low — atribuição, não conteúdo, e por isso
   fora do piso de 32px como .t-ref. */
#S30 .s30__mito { color: var(--txt-mid) }
#S30 .s30__assina { color: var(--txt-low); letter-spacing: var(--tr-label) }

/* --- S30B · A tese em imagem · 0:15 · cena intercalada (14/08) ---------- */
/* O vídeo cobre o palco inteiro e o fundo da cena é o MESMO --bg-900 em que o
   fade final do arquivo termina: quando ele esvaece, o que sobra É a cena —
   nenhuma costura visível entre o último frame e o palco. */
#S30B { background: var(--bg-900) }
#S30B .tese-video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; background: var(--bg-900);
}

/* --- S31 · Encerramento · 0:15 ----------------------------------------- */
#S31 .qr-grid { grid-template-columns: 1fr 1fr; gap: var(--sp-6); align-content: start }
/* 13/08: o cartão perdeu respiro INTERNO — padding de sp-4 para sp-3 e gap de
   sp-3 para sp-2 — para pagar a linha de contato que entrou no fecho. São 40px
   dos 56 que ela custou; o resto veio do gap do stack e da margem da própria
   linha. O QUE NÃO ENCOLHEU foi o símbolo: `#S31 .card-qr__code` continua em
   24rem, porque 384px dá 6,30 px por módulo contra um piso de 6,00 (gerar-qr
   §PISO_PX) — 23rem já cairia para 6,03, e a área escaneável é a única coisa
   desta cena que não se negocia por layout. Sem isso o "Ciência que
   transforma." saía 4px abaixo da borda do palco. */
#S31 .card-qr { padding: var(--sp-3); gap: var(--sp-2) }
#S31 > .u-stack { gap: var(--sp-2) }
/* 24rem, e não 20rem: o QR do agente passou a carregar a mensagem que já vem
   digitada no WhatsApp, e isso o levou de versão 4 (33×33) para versão 9
   (53×53) — mais módulos no mesmo espaço é módulo menor. A 20rem cada módulo
   ficava com 5,25 px, abaixo do piso de 6 px que o storyboard exige para
   leitura do fundo da sala; a 24rem são 6,30 px. Quem manda no número aqui é a
   distância de leitura, não a composição. */
#S31 .card-qr__code { width: 24rem; height: 24rem }
#S31 .closing { display: grid; gap: var(--sp-2); justify-items: center; text-align: center }
#S31 .motto { font-style: italic; color: var(--txt-low) }

/* A LINHA DE CONTATO (13/08/2026). Três itens numa linha só: o WhatsApp e os
   dois Instagram. Em --fs-body (32px) porque é o piso de §5.1 e porque este é
   o único texto do deck que alguém vai querer LER e ANOTAR do fundo da sala —
   quem não escanear o QR vai copiar daqui à mão.

   `flex-wrap` e não `nowrap`: em 1920 os três cabem em ~990px de 1728, mas o
   handle mais longo tem 20 caracteres e um dia pode crescer; quebrar em duas
   linhas centradas é degradação aceitável, cortar não é.

   Sem sublinhado: no projetor, sublinhado a 32px vira ruído sobre o navy, e
   ninguém clica num telão. O affordance de link existe para o celular, e lá
   ele aparece no :hover/:active e no cursor. */
#S31 .contatos {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--sp-1) var(--sp-5);
}
#S31 .contato {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  color: var(--txt-mid); text-decoration: none;
  font: 500 var(--fs-body)/1.2 var(--font-body);
  transition: color var(--dur-fast);
}
#S31 .contato > svg { width: var(--sp-4); height: var(--sp-4); flex: none; color: var(--primary-soft) }
#S31 .contato:hover, #S31 .contato:focus-visible { color: var(--txt-high) }
#S31 .capa__brand { position: absolute; right: var(--margin-h); bottom: var(--margin-v) }

/* ============================================================================
   DEGRADAÇÕES DE CENA
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .ecg__line, .ecg__qrs, .ecg--loop .ecg__line,
  .fog-word, .net-pulse, .net-node, .tf-chev path,
  .track__item[data-live="1"] .track__node::after,
  .bundle__item[data-state="late"]::after,
  .typing i { animation: none !important }
  .ecg__line, .bed-silhouette path { stroke-dashoffset: 0 }
  .words__w { opacity: 1; transform: none; animation: none !important }
  /* a órbita da S30 não pode perder o transform: ele é a posição, não a entrada */
  #S30 .orbit__w { opacity: 1; animation: none !important }
}
:root[data-motion="reduced"] .fog-word,
:root[data-motion="reduced"] .net-pulse,
:root[data-motion="reduced"] .net-node,
:root[data-motion="reduced"] .typing i,
:root[data-motion="reduced"] .bundle__item[data-state="late"]::after { animation: none !important }

/* Mobile (§4.9): as cenas que são composição absoluta viram blocos em fluxo.
   O que era posição no palco vira ordem de leitura. */
@media (max-width: 900px) {
  .formula-band, .rule-30, .grid-fluid, #S10 .tl, #S18 .prot, #S18 .prot__rail,
  .bed-silhouette, .ecg, .ring, #S09 .promise, .fog-words, .band-warn, .tarja-acc,
  #S21 .back-mark, #S22 .s22__title, .infra-row, .fallback-video,
  .provas, .provas__nota, #S11 .escada, .degrau {
    position: static; inset: auto; width: auto; height: auto; transform: none;
  }
  /* O filme do fechamento (S30B) no celular: bloco em fluxo, largura cheia,
     altura da própria razão 16:9 do arquivo. */
  #S30B .tese-video { position: static; width: 100%; height: auto }
  /* O filme da S14 não: ele é a batida 1 pintada por cima do palco, e no modo
     de leitura não há batida — os estados finais já estão em fluxo. */
  #S14 .s14-filme { display: none }
  /* A ESCADA da S11 no celular: a altura era o argumento (força da prova) e
     no modo de leitura não há altura — vira ordem. Os degraus empilham na
     mesma sequência cronológica, cada um com o filete lateral que já os
     distingue, e o SCREEN continua sendo o único em --accent. */
  #S11 .escada { display: grid; gap: var(--sp-2); margin-top: var(--sp-3) }
  #S11 .escada::before, #S11 .escada__eixo, .degrau::after { display: none }
  .degrau { padding: var(--sp-2); background: var(--bg-700) }
  .degrau__selo { opacity: 1 !important; transform: none !important }
  /* O bloco de definição da S12 quebra em duas linhas em vez de estourar. */
  .split-arrow--gen { flex-wrap: wrap; justify-content: flex-start }
  .gen-stat { flex-wrap: wrap }
  /* A faixa de provas no celular: os cards viram a bibliografia daquela cena,
     empilhada e inteira. É a única superfície do deck em que o título do
     artigo é para ler de verdade — por isso o clamp sai e a autoria desce
     para o lugar de legenda. As duas faixas da S11 aparecem as DUAS: no modo
     de leitura não há batida, e esconder metade das referências seria
     esconder prova de fonte. */
  .provas { display: grid; gap: var(--sp-2); margin-top: var(--sp-3) }
  .provas[data-em-beat] { opacity: 1 !important; pointer-events: auto !important }
  .prova { padding: .75rem .875rem }
  .prova__t { -webkit-line-clamp: unset; font-size: .9375rem; line-height: 1.35 }
  .prova__a { font-size: .8125rem; white-space: normal }
  /* No mobile a ordem de leitura é autoria depois do título, como numa
     bibliografia — o card deixa de ser rótulo de telão e vira entrada. */
  .prova { flex-direction: column-reverse }
  .ring, .bed-silhouette, .fog-words, #S21 .back-mark { display: none }
  /* .act-tag é absoluto em base.css com top/left nas margens do palco — no
     mobile as margens são 0 e a cena é static, então TODOS os rótulos de ato
     ancoravam no .stage e empilhavam ilegíveis no canto (0,0) da página, por
     cima da capa. Em fluxo, cada um vira o rótulo de abertura da sua cena. */
  .act-tag { position: static; display: block; margin: 0 0 1rem }
  /* S08: com o tabuleiro repetido escondido (worlds.css §mobile), as quatro
     palavras — o conteúdo próprio da cena — viram lista estática. #S08
     (1-0-1) ganha do display:none de classe pura acima. */
  #S08 .fog-words {
    display: grid; position: static; inset: auto; width: auto; height: auto;
    transform: none; overflow: visible; pointer-events: auto; gap: .75rem;
  }
  #S08 .fog-word { position: static; animation: none; transform: none; white-space: normal }
  /* S09: o selo 'ATO IV · 12:15' é absoluto; com o card estático e o
     tabuleiro escondido ele ancoraria no fim da cena, por cima das notas. */
  #S09 .promise__back { position: static; margin-top: .5rem; justify-content: center }
  /* Camada da curva da S12 (u-abs u-full, inset:0): com .scene estática o
     absoluto ancora no .stage e cobre a página INTEIRA — e engole todo toque
     (nota não abre, link da bibliografia não funciona). Some na leitura. */
  #S10 > svg[aria-hidden="true"], #S10 > svg.u-full { display: none }
  /* S12: o reset lá de cima só tira o .tl do absoluto; marcos e rótulos
     seguiam absolutos com translateX(-50%) — sete rótulos empilhados num
     traço de altura zero, dois vazando pela esquerda. Vira lista vertical. */
  #S10 .tl { height: auto; display: grid; gap: var(--sp-4); margin: 1rem 0 }
  #S10 .tl::before { display: none }
  #S10 .tl__mark { position: static; transform: none; display: flex; align-items: center; gap: var(--sp-3) }
  #S10 .tl__node { transform: none; flex: none }
  #S10 .tl__mark[data-key="1"] .tl__node { transform: scale(1.4); transform-origin: left center }
  #S10 .tl__label { position: static; transform: none; width: auto; text-align: left; top: auto; bottom: auto }
  .band-warn, .tarja-acc, #S19B .duo2 { opacity: 1; transform: none; margin: 1rem 0 }
  /* A referência da cena é absoluta com bottom ancorado na base do palco; na
     página de leitura ela imprimia por cima do "O que o palestrante diz aqui"
     e BLOQUEAVA o toque no toggle em 12 cenas. Genérico de propósito: regra
     por cena deixaria buraco (a varredura de toque achou a S31 fora das
     listas). O `bottom` de #S29 .scene__ref morre sozinho em static. */
  .scene__ref { position: static; margin-top: 1.5rem; overflow-wrap: break-word }
  /* O callback aos palestrantes do dia (S17 Singer, S28 Kalil) era absoluto
     no canto do palco e sumia da própria cena no fluxo. width:auto DESFAZ o
     max-content da regra base: com ele, o chip da S28 mede 395px num viewport
     de 390 e sai cortado (medido); inline-block encolhe ao conteúdo e o teto
     em 100vw faz o texto longo quebrar em duas linhas visíveis. */
  .chip-callback, .chip-callback--corner {
    position: static; display: inline-block; width: auto;
    max-width: calc(100vw - 2.5rem); margin: 1rem 0 0;
  }
  /* S01: rodapé e logo da capa são absolutos no palco; em fluxo eles entram
     na ordem de leitura (data, logo) em vez de cobrir o link das notas. */
  #S01 .capa__foot { position: static; margin: 1.5rem 0 0 }
  #S01 .capa__brand { position: static; margin: 1rem 0 0 }
  /* `#S04 .qr-grid` na lista, e não só `.qr-grid`: a regra base usa o ID e
     ganha da classe pura por especificidade — foi assim que o segundo QR
     ficou cortado na borda do celular sem ninguém notar. */
  #S02 .bio, #S11 .duo, #S19B .duo2, #S29 .duo2, #S25 .demo,
  #S04 .qr-grid, .qr-grid, .evidence {
    grid-template-columns: 1fr;
  }
  /* margin-top: a aba numerada sobe 2rem acima do 1º cartão e mordia o
     subtítulo — a folga reserva o espaço dela. */
  #S04 .qr-grid { gap: var(--sp-6); height: auto; margin-top: 1.5rem }
  /* A grade 3×3 (S10/S13) mantinha três colunas: com rótulos nowrap o
     min-content das trilhas soma ~540px — a coluna do meio era cortada na
     borda e a terceira (ChatGPT, Copilot, Cálculo) ficava INTEIRA fora da
     tela. No celular a grade é uma coluna. */
  .grid-fluid { grid-template-columns: 1fr; grid-template-rows: none }

  /* --- S03 · o vermelho vira fundo da cena -------------------------------
     O .drain é um véu absoluto que cobre o palco inteiro; no modo de leitura
     as cenas são estáticas e ele se ancora no .stage — o resultado era a
     página INTEIRA pintada de vermelho, da capa ao encerramento (visto em
     produção, 11/08). No celular o vermelho vira fundo da própria cena: a
     identidade da única cena vermelha fica, o vazamento morre. */
  #S03 { background: var(--accent) }
  #S03 .drain { display: none }
  #S18 .prot { grid-template-rows: none }
  /* sem batidas no fluxo de leitura: quem registra cada item É o argumento
     da S18 — nasce visível, como a tarja e a band-warn logo acima. E a faixa
     'IA preditiva → IA generativa' da S10 renderizava como barra vazia:
     rótulo gateado por batida que nunca chega. */
  .prot__who { opacity: 1; transform: none }
  .split-arrow { opacity: 1 }
  .bundle, .kpi-row, #S15 .defenses { grid-auto-flow: row }
  /* S15: a fileira de chips não quebrava linha; o min-content dela alargava
     a trilha do u-stack para ~800px — Gemini/Copilot/OpenEvidence fora da
     tela e o cartão "Alucinação" cortado na borda direita. */
  #S15 .chips-row { flex-wrap: wrap }
  #S15 .chips-row .brand-chip { height: auto; padding: var(--sp-2) var(--sp-3); font-size: var(--fs-label) }
  /* S25: 78% de 348px quebra o alerta a cada 1-3 palavras; no celular o balão
     usa a largura toda do feed. E o selo SIMULAÇÃO esmagava o kicker a 0px. */
  .bubble { max-width: 100% }
  .sim__head { flex-wrap: wrap }
  .flow { flex-direction: column }
  /* S11: o AUC em 72px de palco quebrava em três linhas gigantes, e o
     min-height de projeção (38.75rem) deixava ~400px vazios nos cartões. */
  .duo__stat { font-size: 2rem }
  .duo__col { min-height: 0 }
  /* S23: sem batida no fluxo, a régua de escalada 1h/3h/6h tem de existir —
     e o nowrap dela era o min-content de 445px que estourava a cena inteira. */
  .ruler { opacity: 1; transform: none; flex-wrap: wrap }
  .card-qr__code { width: 100%; height: auto }
  /* S22: os 4 chips nominativos somam mais que o viewport; sem quebra eram
     cortados nas DUAS bordas. */
  .infra-row { flex-wrap: wrap }
  /* S31: as regras base com ID (#S31 .qr-grid, #S31 .card-qr__code) vencem
     as linhas genéricas — mesmo caso do #S04. A pill nowrap do encerramento
     tem min-content de 468px e alargaria a trilha sozinha. */
  #S31 .qr-grid { grid-template-columns: 1fr }
  #S31 .card-qr__code { width: 100%; height: auto }
  #S31 .pill { white-space: normal; text-align: center }
  /* A linha de contato empilha no celular: três itens de 32px lado a lado
     estouram 390px de largura, e é NO CELULAR que eles servem para alguma
     coisa — é lá que se toca neles. Em coluna, cada um vira um alvo de toque
     de linha inteira. */
  #S31 .contatos { flex-direction: column; align-items: center; gap: var(--sp-2) }
  #S05 .track { flex-wrap: wrap; height: auto }
  /* Na pilha vertical o scale(1.25) do nó vivo mordia o rótulo da parada
     anterior, e o trilho horizontal virava um risco pelo primeiro nó. */
  #S05 .track__item[data-live="1"] .track__node { transform: none }
  #S05 .track::before { display: none }
  /* S24: a fileira de semáforos nowrap dita o min-content da trilha do grid
     (456px) e alarga a cena inteira; as faixas de palco dispensam altura
     mínima no fluxo de leitura. */
  #S24 .lights { flex-wrap: wrap }
  #S24 .band-title, #S24 .band-content, #S24 .band-data { min-height: 0 }
  /* Régua fluida, não de 390: em 768 (tablet) o width:100% fazia o monograma
     FC virar moldura vazia de 804x1005px. Teto na largura que a cena tem aos
     390px; centrado, mantém escala de retrato em qualquer largura ≤900. */
  .bio__portrait { width: 100%; max-width: 22rem; margin-inline: auto; height: auto; aspect-ratio: 4 / 5 }
  /* `#S19B .chat-mock` saiu daqui em 12/08: o mock migrou para a S27 e a
     regra de lá (linha ~1565) já o cobre. Seletor que não casa com nada é
     armadilha — o próximo a mexer acha que a S19B ainda tem um mock. */
  .net { width: 100%; height: auto }
  /* S13 no fluxo de leitura: o morph nunca roda. A rede (opacity 0 inline no
     HTML) e o transformador (visibility hidden) deixavam ~600px de cena
     vazia — e a rede é o payoff da cena. Ela liga; o resto sai do fluxo. */
  #S13 .net { opacity: 1 !important }
  #S13 .tf-wrap, #S13 .morph-canvas { display: none }
  /* S14: a rede decorativa da coluna direita é absoluta dentro de uma coluna
     que colapsa no modo de leitura — caía por cima da tarja "O modelo não
     sabe do seu paciente". É aria-hidden: some. Desde 13/08 a cena tem
     coreografia por [data-beat]: no fluxo de leitura os três blocos nascem
     visíveis e a RESPOSTA nasce visível em fluxo, depois deles — os
     seletores com [data-beat] igualam a especificidade das regras de palco
     (id+attr+classe), que senão venceriam este bloco (armadilha 2 do
     registro de 11–12/08). */
  #S14 .net--small { display: none }
  #S14 .resposta, #S14[data-beat] .resposta {
    position: static; opacity: 1; transform: none; width: auto; box-shadow: none;
  }
  #S14 .s14-entrada .tagged, #S14[data-beat] .s14-entrada .tagged {
    opacity: 1; transform: none;
  }
  /* 13/08, revisão mobile (item B): a etiqueta VERTICAL estourava o card no
     fluxo — "GROUNDING" precisa de ~180px de altura e o card de uma linha tem
     ~120px; a palavra invadia o card seguinte. No celular a etiqueta vira
     faixa horizontal no topo do card. Vale para os 4 (Prompt, Contexto,
     Grounding, Resposta). */
  .tagged { padding-left: 0 }
  .tagged__tag {
    position: static; width: auto; writing-mode: horizontal-tb;
    display: inline-flex; padding: .375rem .75rem; margin-bottom: .75rem;
  }
  /* 13/08 (item G): no fluxo o trilho da S05 empilhava círculo e rótulo com o
     rótulo ENTRE dois círculos — parecia pertencer ao de baixo. Círculo e
     rótulo lado a lado, sem a linha do trilho (que é horizontal de palco). */
  .track { display: grid; grid-template-columns: 1fr; gap: 1rem }
  .track::before { display: none }
  .track__item {
    display: flex; align-items: center; gap: 1rem;
    width: 100%; text-align: left;
  }
  .track__node { flex: none }
  .track__label { text-align: left }
  /* 13/08 (item F): a capa centrada em height:100% de palco abria vazio no
     fluxo. */
  #S01 .capa { height: auto }
  /* S28: colunas de 11rem eram medida de palco — 848px num viewport de 390
     deixavam γ e δ (o 40% em --accent, o dado da cena) fora da tela. Fluidas,
     as 4 barras cabem e as alturas (--h) continuam proporcionais. */
  #S28 .bars { height: auto; gap: var(--sp-2) }
  #S28 .bars__col { width: auto; flex: 1 1 0 }
  /* Reforma de 10/08: a barra do intensivista (S19B) e a linha do tempo da zona
     cega (S19) têm largura de palco em rem — no celular viram largura fluida,
     e a coreografia de batida 0 da barra não se aplica ao fluxo de leitura. */
  #S19 .blindline { width: 100% }
  /* 13/08: com as QUATRO fatias rotuladas, a barra proporcional não cabe em
     390px — o min-width:auto do flex item faz "37% computador" (min-content
     ~171px) vencer a fração de 36,7% (~131px) e empurrar "17% leito" para fora
     (pego pelo verifica-mobile: seg em 359..417). No fluxo de leitura a barra
     vira LISTA vertical: as cores por data-seg continuam dizendo quem é quem,
     e o número está no rótulo — largura proporcional é argumento de palco. */
  #S19B .timebar { width: 100%; height: auto; flex-direction: column }
  #S19B .timebar__seg { flex: 0 0 auto; padding: var(--sp-2) var(--sp-1) }
  /* S19B: os 11rem de padding eram reserva para o rodapé ABSOLUTO da projeção;
     no fluxo o rodapé é estático e a reserva virava 176px de vazio. */
  #S19B .duo2 { padding-bottom: 0; height: auto }
  /* S29: o piso de 26rem e o space-between são escala de palco de 1080px —
     no celular espalhavam 3 linhas curtas por 416px de cartão. */
  #S29 .guide-card, #S29 .evidence { min-height: 0 }
  #S29 .u-stack > .card-glass { align-content: start }
  #S29 .duo2 { height: auto }
  /* S29B: a cena nasceu em 13/08 e o modo de leitura precisa da mesma
     desmontagem das vizinhas — sem ela a segunda coluna media 264..464 num
     viewport de 390 e saía pela direita (seis elementos, pegos pelo
     verifica-mobile no primeiro build). */
  /* S29B: a tabela de normas vira lista — o rótulo por cima do texto, não ao
     lado. Em 390px a coluna fixa de 20rem sozinha comeria 82% da tela. */
  #S29B .normas { height: auto; padding: 0; align-content: start; gap: 2rem }
  #S29B .norma { grid-template-columns: 1fr; gap: var(--sp-2); padding-bottom: 1.5rem }
  #S19B .intensivista { transform: none !important; grid-template-rows: auto auto !important }
  /* S30: a órbita por transform joga SEGURA/EFICIENTE para fora do viewport
     e PADRONIZADA sobre o ECG — na leitura as 4 palavras viram um bloco. */
  #S30 .orbit { min-height: 0; display: flex; flex-wrap: wrap;
    gap: .5rem 1.25rem; justify-content: center; align-items: baseline }
  #S30 .orbit__w { position: static; transform: none; opacity: 1; animation: none }

  /* --- S05B · a mesa vira bibliografia -----------------------------------
     No celular não existe mesa: 89 cards absolutos com transform viram uma
     pilha ilegível de papel num viewport de 390px. O que a plateia quer ao
     abrir o deck pelo QR é a LISTA — os grupos como seções, cada referência
     como um link que abre o artigo. Os divisores de grupo já estão no DOM
     (injetados pelo script da cena, display:none na projeção). */
  #S05B { overflow: visible }
  #S05B .mesa, #S05B .mesa__pilha, #S05B .mesa__eyebrow, #S05B .mesa__pe {
    position: static; inset: auto; width: auto; height: auto;
  }
  #S05B .mesa__rotulos { display: none }   /* os rótulos da projeção */
  .card-art {
    position: static; transform: none !important;
    transition: none; animation: none !important; will-change: auto;
    width: auto; height: auto; min-height: 0; z-index: auto;
    margin: 0 0 .625rem;
  }
  .card-art__t { -webkit-line-clamp: unset }
  .mesa__divisor {
    display: flex; align-items: baseline; justify-content: space-between;
    margin: 1.75rem 0 .75rem; padding-bottom: .375rem;
    border-bottom: 1px solid var(--stroke-hi);
  }
  .mesa__divisor b {
    font: 700 1rem/1 var(--font-display); letter-spacing: .06em;
    color: var(--txt-high);
  }
  .mesa__divisor i {
    font: 700 1rem/1 var(--font-display); font-style: normal;
    color: var(--primary-soft);
  }
  /* No toque não há hover: o card inteiro já é o link. O piso de leitura sobe
     um degrau em relação ao .t-ref porque aqui o card É o conteúdo. */
  .card-art__t { font-size: .9375rem; line-height: 1.35 }
  .card-art__a { font-size: .8125rem }
}

@media print {
  .ecg__line, .bed-silhouette path { stroke-dashoffset: 0; animation: none }
  .words__w, .bubble { opacity: 1; transform: none; animation: none }
  #S30 .orbit__w { opacity: 1; animation: none }
  .fog-words, .fallback-video, .morph-canvas { display: none }
  /* Reforma de 10/08: componentes com gate de batida precisam existir no PDF
     de backup — sem isto a tarja da S19 (6,6 h), as colunas F2/F3 e a tarja
     âmbar da S19B simplesmente não imprimem. */
  .tarja-acc, .band-warn, #S19B .duo2 { opacity: 1; transform: none }
}
