Scroll não é tempo
Mapear scroll em tempo de vídeo linearmente deixa a cena passando rápido demais justo onde a copy está no pico. O linger remapeia o tempo por cena com uma função monótona, a câmera assenta no meio e acelera nas bordas, sem tocar os frames de emenda (f(0)=0, f(1)=1).
Host sem byte-range congela o vídeo
Sem Range HTTP, video.seekable fica em [0,0] e todo seek cai no frame 0. O motor baixa cada clipe como Blob e toca de um object URL, sempre seekável, e só dispara o fetch perto da cena: 1,6 viewport nas páginas de projeto, 0,6 na trilha, onde as cenas são curtas. Medido no meio da trilha: no máximo quatro clipes vivos, e zero depois que ela termina.
iOS pinta a tela em branco
Vídeo mudo que nunca deu play não pinta frame após seek no Safari. O still fica como poster até o evento seeked, não o loadedmetadata, e cada vídeo é "aquecido" (play→pause) no primeiro toque.
Nove cenas seguram 30 MB
O motor original nunca chamava revokeObjectURL. Acrescentei o descarregamento: a cena que sai do dobro da janela de carga (1,2 viewport na trilha) devolve o blob e volta ao still, a distância dupla evita carregar e descarregar na borda. Rolar de volta recarrega.
Reduced-motion custa zero byte
Sob prefers-reduced-motion o motor não baixa nenhum vídeo: as 27 stills fazem crossfade entre si. Acessibilidade e desempenho pela mesma porta.
Emenda entre cenas
Dentro de cada projeto, a cena B começa no último frame real da cena A (frame-lock, medido em 17-21 dB, composição idêntica, só grão). Entre projetos diferentes não há clipe de ligação: a trilha usa crossfade direto e assume isso, em vez de fingir um voo contínuo que não existe.