primocode 10.16.0 → 10.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/LICENSES/edit-ia-pro.txt +32 -0
  2. package/LICENSES/fontes-OFL.txt +476 -0
  3. package/LICENSES/hyperframes-Apache-2.0.txt +205 -0
  4. package/LICENSES/permanent-marker-Apache-2.0.txt +204 -0
  5. package/LICENSES/selfie-multiclass-Apache-2.0.txt +227 -0
  6. package/LICENSES/three-MIT.txt +23 -0
  7. package/LICENSES/video-use-MIT.txt +24 -0
  8. package/LICENSES/woven-sfx-CC0.txt +16 -0
  9. package/LICENSES/yunet-MIT.txt +21 -0
  10. package/README.md +2 -2
  11. package/bin/primocode.js +11 -2
  12. package/editor/LICENSE.md +11 -0
  13. package/editor/README.md +321 -0
  14. package/editor/SKILL.md +307 -0
  15. package/editor/THIRD_PARTY_NOTICES.md +228 -0
  16. package/editor/assets/baixar.json +591 -0
  17. package/editor/assets/fonts/anton/OFL.txt +93 -0
  18. package/editor/assets/fonts/caveat/OFL.txt +93 -0
  19. package/editor/assets/fonts/figtree/OFL.txt +93 -0
  20. package/editor/assets/fonts/fonts.json +10 -0
  21. package/editor/assets/fonts/montserrat/OFL.txt +93 -0
  22. package/editor/assets/fonts/permanentmarker/LICENSE.txt +202 -0
  23. package/editor/assets/models/SELFIE_MULTICLASS_LICENSE.txt +227 -0
  24. package/editor/assets/models/YUNET_LICENSE.txt +21 -0
  25. package/editor/assets/sfx/licencas/bass_drop.txt +32 -0
  26. package/editor/assets/sfx/licencas/cash.txt +76 -0
  27. package/editor/assets/sfx/licencas/charge.txt +31 -0
  28. package/editor/assets/sfx/licencas/click.txt +46 -0
  29. package/editor/assets/sfx/licencas/coin.txt +50 -0
  30. package/editor/assets/sfx/licencas/count.txt +47 -0
  31. package/editor/assets/sfx/licencas/data.txt +31 -0
  32. package/editor/assets/sfx/licencas/error.txt +32 -0
  33. package/editor/assets/sfx/licencas/flash.txt +31 -0
  34. package/editor/assets/sfx/licencas/gears.txt +82 -0
  35. package/editor/assets/sfx/licencas/keyboard.txt +48 -0
  36. package/editor/assets/sfx/licencas/metal_hit.txt +62 -0
  37. package/editor/assets/sfx/licencas/metal_riser.txt +61 -0
  38. package/editor/assets/sfx/licencas/mouse_click.txt +50 -0
  39. package/editor/assets/sfx/licencas/notification.txt +54 -0
  40. package/editor/assets/sfx/licencas/pop.txt +38 -0
  41. package/editor/assets/sfx/licencas/rewind.txt +32 -0
  42. package/editor/assets/sfx/licencas/rise.txt +46 -0
  43. package/editor/assets/sfx/licencas/sinteticos.txt +21 -0
  44. package/editor/assets/sfx/licencas/snap.txt +50 -0
  45. package/editor/assets/sfx/licencas/success.txt +40 -0
  46. package/editor/assets/sfx/licencas/synth_hit.txt +32 -0
  47. package/editor/assets/sfx/licencas/tick.txt +14 -0
  48. package/editor/assets/sfx/licencas/whoosh.txt +47 -0
  49. package/editor/assets/sfx/licencas/wind.txt +31 -0
  50. package/editor/assets/sfx/sfx.json +832 -0
  51. package/editor/assets/sfx/sfx_sinteticos.json +317 -0
  52. package/editor/assets/sfx/sfx_woven.json +387 -0
  53. package/editor/assets/sfx/woven/LICENCA.md +16 -0
  54. package/editor/fichas/briefing-do-video.md +45 -0
  55. package/editor/fichas/conferencia-da-amostra.md +56 -0
  56. package/editor/fichas/matriz-de-ganchos.csv +3 -0
  57. package/editor/fichas/padrao.json +16 -0
  58. package/editor/fichas/planilha-de-cenas.csv +4 -0
  59. package/editor/fichas/planilha-de-versoes.csv +4 -0
  60. package/editor/fichas/plano-em-blocos.md +32 -0
  61. package/editor/fichas/registro-de-materiais.json +27 -0
  62. package/editor/helpers/assets_ocr/ocr_vision.swift +41 -0
  63. package/editor/helpers/bordas_precisas.py +272 -0
  64. package/editor/helpers/camera_viva.py +426 -0
  65. package/editor/helpers/captions.py +601 -0
  66. package/editor/helpers/common.py +308 -0
  67. package/editor/helpers/compose_final.py +3555 -0
  68. package/editor/helpers/conferir_corte.py +234 -0
  69. package/editor/helpers/conferir_dados.py +405 -0
  70. package/editor/helpers/contact_sheet.py +282 -0
  71. package/editor/helpers/corte_por_onda.py +433 -0
  72. package/editor/helpers/corte_seco.py +595 -0
  73. package/editor/helpers/denoise.py +971 -0
  74. package/editor/helpers/efeitos.py +408 -0
  75. package/editor/helpers/efeitos_gerar.py +159 -0
  76. package/editor/helpers/elevenlabs_api.py +442 -0
  77. package/editor/helpers/face_layout.py +570 -0
  78. package/editor/helpers/fetch_images.py +423 -0
  79. package/editor/helpers/ganho_por_trecho.py +214 -0
  80. package/editor/helpers/gen_image.py +312 -0
  81. package/editor/helpers/gen_music.py +316 -0
  82. package/editor/helpers/gen_sfx.py +148 -0
  83. package/editor/helpers/grade.py +307 -0
  84. package/editor/helpers/icloud.py +119 -0
  85. package/editor/helpers/make_sfx.py +940 -0
  86. package/editor/helpers/matte.py +1342 -0
  87. package/editor/helpers/medir_referencia.py +2232 -0
  88. package/editor/helpers/mixar_trilha.py +354 -0
  89. package/editor/helpers/montar_corte.py +295 -0
  90. package/editor/helpers/motion/__init__.py +13 -0
  91. package/editor/helpers/motion/engine.py +1915 -0
  92. package/editor/helpers/motion/registry.py +510 -0
  93. package/editor/helpers/motion/render.py +665 -0
  94. package/editor/helpers/motion/templates/barra_progresso.py +370 -0
  95. package/editor/helpers/motion/templates/carimbo.py +132 -0
  96. package/editor/helpers/motion/templates/checklist.py +271 -0
  97. package/editor/helpers/motion/templates/comentario_cta.py +380 -0
  98. package/editor/helpers/motion/templates/contador.py +222 -0
  99. package/editor/helpers/motion/templates/destaque.py +263 -0
  100. package/editor/helpers/motion/templates/etiqueta_preco.py +461 -0
  101. package/editor/helpers/motion/templates/grafico_barras.py +510 -0
  102. package/editor/helpers/motion/templates/grafico_linha.py +462 -0
  103. package/editor/helpers/motion/templates/notificacao.py +266 -0
  104. package/editor/helpers/motion/templates/palavra_impacto.py +410 -0
  105. package/editor/helpers/motion/templates/passos.py +321 -0
  106. package/editor/helpers/motion/templates/riscado.py +298 -0
  107. package/editor/helpers/motion/templates/selo_garantia.py +150 -0
  108. package/editor/helpers/motion_render.py +241 -0
  109. package/editor/helpers/motion_sugerir.py +961 -0
  110. package/editor/helpers/noise_check.py +134 -0
  111. package/editor/helpers/pack_transcripts.py +223 -0
  112. package/editor/helpers/palavras_na_onda.py +176 -0
  113. package/editor/helpers/plano_sfx.py +839 -0
  114. package/editor/helpers/portao.py +427 -0
  115. package/editor/helpers/preview_server.py +1177 -0
  116. package/editor/helpers/rastrear_rosto.py +298 -0
  117. package/editor/helpers/render_cut.py +1256 -0
  118. package/editor/helpers/respiros.py +89 -0
  119. package/editor/helpers/sincronizar_cameras.py +571 -0
  120. package/editor/helpers/speech_regions.py +423 -0
  121. package/editor/helpers/style_catalog.json +231 -0
  122. package/editor/helpers/style_samples.py +246 -0
  123. package/editor/helpers/tela_verde.py +2113 -0
  124. package/editor/helpers/text_render.py +2059 -0
  125. package/editor/helpers/texto_na_tela.py +366 -0
  126. package/editor/helpers/timeline_view.py +292 -0
  127. package/editor/helpers/tira_de_quadros.py +113 -0
  128. package/editor/helpers/transcribe.py +734 -0
  129. package/editor/helpers/transcribe_batch.py +129 -0
  130. package/editor/helpers/trilhas.py +1146 -0
  131. package/editor/helpers/trilhas_humores.json +66 -0
  132. package/editor/helpers/valores.py +187 -0
  133. package/editor/helpers/verify_cut.py +820 -0
  134. package/editor/helpers/voice_levels.py +233 -0
  135. package/editor/helpers/voz_check.py +386 -0
  136. package/editor/helpers/voz_limpa.py +81 -0
  137. package/editor/helpers/watch_preview.py +372 -0
  138. package/editor/modelo-projeto/LEIA.md +402 -0
  139. package/editor/modelo-projeto/assets/fontes/OFL-Caveat.txt +93 -0
  140. package/editor/modelo-projeto/assets/fontes/OFL-JetBrainsMono.txt +93 -0
  141. package/editor/modelo-projeto/assets/fontes/OFL-Montserrat.txt +93 -0
  142. package/editor/modelo-projeto/assets/fontes/larguras.json +1 -0
  143. package/editor/modelo-projeto/assets/three/BufferGeometryUtils.js +1438 -0
  144. package/editor/modelo-projeto/assets/three/GLTFLoader.js +4895 -0
  145. package/editor/modelo-projeto/assets/three/LICENSE +21 -0
  146. package/editor/modelo-projeto/assets/three/ORIGEM.txt +17 -0
  147. package/editor/modelo-projeto/assets/three/RoomEnvironment.js +185 -0
  148. package/editor/modelo-projeto/assets/three/three.core.min.js +6 -0
  149. package/editor/modelo-projeto/assets/three/three.module.min.js +6 -0
  150. package/editor/modelo-projeto/build.py +4828 -0
  151. package/editor/modelo-projeto/compositions/cenas/2d/k2d_antes_depois.html +199 -0
  152. package/editor/modelo-projeto/compositions/cenas/2d/k2d_arquivo.html +232 -0
  153. package/editor/modelo-projeto/compositions/cenas/2d/k2d_busca.html +315 -0
  154. package/editor/modelo-projeto/compositions/cenas/2d/k2d_comando.html +304 -0
  155. package/editor/modelo-projeto/compositions/cenas/2d/k2d_conversa.html +217 -0
  156. package/editor/modelo-projeto/compositions/cenas/2d/k2d_esquema.html +289 -0
  157. package/editor/modelo-projeto/compositions/cenas/2d/k2d_medidor.html +249 -0
  158. package/editor/modelo-projeto/compositions/cenas/2d/k2d_notas.html +233 -0
  159. package/editor/modelo-projeto/compositions/cenas/3d/k3d_caminho.html +197 -0
  160. package/editor/modelo-projeto/compositions/cenas/3d/k3d_explodida.html +152 -0
  161. package/editor/modelo-projeto/compositions/cenas/3d/k3d_tela.html +167 -0
  162. package/editor/modelo-projeto/compositions/cenas/3d/k3d_volume.html +124 -0
  163. package/editor/modelo-projeto/compositions/cenas/3d/motor-three.js +728 -0
  164. package/editor/modelo-projeto/compositions/cenas/LEIA.md +177 -0
  165. package/editor/modelo-projeto/ferramentas/medir_fontes.py +65 -0
  166. package/editor/modelo-projeto/ferramentas/pessoa_recortada.py +176 -0
  167. package/editor/modelo-projeto/ferramentas/rastrear_maos.py +189 -0
  168. package/editor/modelo-projeto/hyperframes.json +12 -0
  169. package/editor/modelo-projeto/package.json +11 -0
  170. package/editor/modelo-projeto/projeto.json +16 -0
  171. package/editor/preview/app.css +1009 -0
  172. package/editor/preview/app.js +2122 -0
  173. package/editor/preview/index.html +307 -0
  174. package/editor/pyproject.toml +23 -0
  175. package/editor/references/3d.md +236 -0
  176. package/editor/references/animacao-catalogo.md +672 -0
  177. package/editor/references/animacao.md +498 -0
  178. package/editor/references/comandos.md +92 -0
  179. package/editor/references/corte-seco-como-funciona.md +50 -0
  180. package/editor/references/corte-seco.md +88 -0
  181. package/editor/references/corte.md +290 -0
  182. package/editor/references/direcao-por-blocos.md +218 -0
  183. package/editor/references/enquadramento.md +654 -0
  184. package/editor/references/estilos-prontos.md +355 -0
  185. package/editor/references/estudio.md +100 -0
  186. package/editor/references/exemplo-eventos.json +67 -0
  187. package/editor/references/lettering.md +252 -0
  188. package/editor/references/modelos.md +494 -0
  189. package/editor/references/phase2.md +1007 -0
  190. package/editor/references/referencia-aplicar.md +203 -0
  191. package/editor/references/referencia-medir.md +375 -0
  192. package/editor/references/revisao.md +335 -0
  193. package/editor/references/som-e-trilha.md +146 -0
  194. package/editor/references/tabela-de-sons.md +130 -0
  195. package/editor/references/variacoes.md +234 -0
  196. package/editor/uv.lock +1141 -0
  197. package/lib/act.js +36 -8
  198. package/lib/catalogo.js +4 -0
  199. package/lib/chamada-torta.js +81 -0
  200. package/lib/editor-pro.js +786 -0
  201. package/lib/entrega.js +58 -0
  202. package/lib/estudio/conferencia-visual.js +76 -0
  203. package/lib/estudio/diretor.js +6 -1
  204. package/lib/estudio/exportar.js +3 -1
  205. package/lib/estudio/gosto.js +45 -4
  206. package/lib/estudio/index.js +8 -5
  207. package/lib/estudio/kit-ae/base.jsx +119 -13
  208. package/lib/estudio/kit-ae/cenas.jsx +47 -15
  209. package/lib/estudio/kit-ae/guarda.jsx +274 -0
  210. package/lib/estudio/kit-ae/primitivas.jsx +180 -24
  211. package/lib/estudio/maquina-diretor.js +61 -7
  212. package/lib/estudio/maquina-foto.js +177 -0
  213. package/lib/estudio/maquina-guarda.js +207 -0
  214. package/lib/estudio/maquina-layout.js +22 -1
  215. package/lib/estudio/maquina.js +60 -11
  216. package/lib/estudio/referencias.js +1 -1
  217. package/lib/estudio/visao/visao.swift +97 -0
  218. package/lib/estudio/visao.js +60 -0
  219. package/lib/local/cerebro.json +192 -6
  220. package/lib/local/motor.js +5 -2
  221. package/lib/nuvem.js +11 -1
  222. package/lib/passagem.js +50 -0
  223. package/lib/perigo.js +19 -11
  224. package/lib/protecao.js +134 -0
  225. package/lib/titulo-conversa.js +46 -0
  226. package/lib/tools.js +3 -0
  227. package/lib/ui.js +1 -1
  228. package/package.json +3 -2
  229. package/skills/editar-meu-video/SKILL.md +6 -2
  230. package/skills/editar-video/SKILL.md +113 -0
  231. package/studio/pessoa.py +52 -0
  232. package/voz/primo_voz/app.py +43 -4
  233. package/voz/primo_voz/chat.html +17 -6
  234. package/voz/primo_voz/painel_dados.py +134 -0
@@ -0,0 +1,315 @@
1
+ <!doctype html>
2
+ <html lang="pt-BR"><head><meta charset="UTF-8"/><title>k2d · barra de busca, resultado e chamada</title></head>
3
+ <body>
4
+ <!--
5
+ Kit 2D (a): uma barra de busca. O termo é digitado letra por letra enquanto a pessoa fala, com o cursor colado na
6
+ última letra digitada. Todas as letras já existem no desenho desde o começo, invisíveis, e só aparecem em ordem: cada
7
+ palavra do termo cai dentro da palavra falada que a chama (ou reparte a duração, se a cena não tiver fala). Na palavra
8
+ do envio a lupa dá um toque, três anéis acendem e um cartão de resultado entra por baixo da barra: o título numa faixa
9
+ na cor de destaque do estilo e uma linha curta embaixo. Por último, na palavra dita, entra o selo da chamada
10
+ ("Comenta [PALAVRA]" é o padrão). O termo, o resultado e a chamada são só parâmetros: o motor não tem nenhum fixo.
11
+ Serve para "pesquisa isso", "digita isso no Google", "comenta a palavra". Só o que a pessoa de fato fala ou pede; o
12
+ cartão não é uma busca de verdade e não prova que o resultado existe.
13
+ Guia: references/animacao.md ("Kit 2D"). Para usar: copie para compositions/cenas/<nome>.html, troque "k2d_busca"
14
+ (template, raiz e ID) e o prefixo "k2db-", mude os PARÂMETROS e ligue no projeto.json em "cenas_proprias".
15
+ Por cima da pessoa: com area: "acima_do_rosto" (e a pessoa em tela cheia) a pilha vira um cartão pequeno acima da
16
+ cabeça, sem fundo, e se ajusta à altura que sobra. O cartão nunca cobre o rosto: se não houver espaço, ou se uma
17
+ "area" escrita à mão tocar o rosto, a cena avisa no console (hf check).
18
+ Fora do 9:16, a raiz leva o tamanho do vídeo (data-width="1920" data-height="1080" no 16:9); o check
19
+ acusa se esquecer.
20
+ -->
21
+ <template id="k2d_busca-template">
22
+ <style>
23
+ @font-face{font-family:Montserrat;src:url("assets/fontes/Montserrat.ttf") format("truetype");font-weight:100 900;font-display:block}
24
+ #root{position:absolute;inset:0;overflow:hidden;font-family:Montserrat}
25
+ #k2db-fundo{position:absolute;inset:0;opacity:0}
26
+ #k2db-bloco{position:absolute;opacity:0}
27
+ #k2db-barra{position:absolute;opacity:0}
28
+ #k2db-borda,#k2db-corpo,#k2db-res-borda{position:absolute}
29
+ #k2db-conteudo{position:absolute;inset:0}
30
+ #k2db-lupa{position:absolute;border-radius:50%}
31
+ #k2db-lupa svg{position:absolute;left:22%;top:22%;width:56%;height:56%;fill:none;stroke:currentColor;stroke-width:5;stroke-linecap:round;stroke-linejoin:round}
32
+ #k2db-lupa-cor{position:absolute;inset:0;border-radius:50%;opacity:0}
33
+ #k2db-texto{position:absolute;display:flex;flex-wrap:wrap;align-content:flex-start;font-weight:800}
34
+ .k2db-palavra{display:inline-block;white-space:nowrap}
35
+ .k2db-letra{opacity:0}
36
+ #k2db-cursor{position:absolute;left:0;top:0;opacity:0}
37
+ .k2db-anel{position:absolute;box-sizing:border-box;border-radius:50%;opacity:0}
38
+ #k2db-resultado{position:absolute;opacity:0}
39
+ #k2db-res-corpo{position:absolute;overflow:hidden}
40
+ #k2db-faixa{position:absolute;left:0;top:0;width:100%;opacity:0}
41
+ #k2db-titulo{position:absolute;font-weight:900;opacity:0}
42
+ #k2db-linha{position:absolute;font-weight:700;opacity:0}
43
+ #k2db-selo{position:absolute;overflow:hidden;opacity:0}
44
+ #k2db-selo-cor{position:absolute;inset:0;opacity:0}
45
+ #k2db-selo-ic{position:absolute;display:flex;align-items:center;justify-content:center;border-radius:50%;opacity:0}
46
+ #k2db-selo-ic svg{width:58%;height:58%;fill:none;stroke:currentColor;stroke-width:4;stroke-linecap:round;stroke-linejoin:round}
47
+ #k2db-selo-txt{position:absolute;white-space:nowrap;font-weight:900;opacity:0}
48
+ </style>
49
+ <div id="root" data-composition-id="k2d_busca" data-width="1080" data-height="1920" data-duration="8">
50
+ <div id="k2db-fundo"></div>
51
+ <div id="k2db-bloco">
52
+ <div id="k2db-resultado">
53
+ <div id="k2db-res-borda"></div>
54
+ <div id="k2db-res-corpo"><div id="k2db-faixa"></div><span id="k2db-titulo"></span><span id="k2db-linha"></span></div>
55
+ </div>
56
+ <div id="k2db-barra">
57
+ <div id="k2db-borda"></div>
58
+ <div id="k2db-corpo"></div>
59
+ <div id="k2db-conteudo">
60
+ <div id="k2db-lupa"><svg viewBox="0 0 44 44"><circle cx="19" cy="19" r="10"/><path d="M27 27L36 36"/></svg><div id="k2db-lupa-cor"><svg viewBox="0 0 44 44"><circle cx="19" cy="19" r="10"/><path d="M27 27L36 36"/></svg></div></div>
61
+ <div id="k2db-texto"></div>
62
+ <div id="k2db-cursor"></div>
63
+ </div>
64
+ </div>
65
+ <div id="k2db-selo"><div id="k2db-selo-cor"></div><div id="k2db-selo-ic"><svg viewBox="0 0 44 44"><path d="M9 11H35A3 3 0 0 1 38 14V27A3 3 0 0 1 35 30H21L13 37V30H9A3 3 0 0 1 6 27V14A3 3 0 0 1 9 11Z"/></svg></div><span id="k2db-selo-txt"></span></div>
66
+ </div>
67
+ </div>
68
+ <script>
69
+ (function () {
70
+ const ID = "k2d_busca";
71
+ // ------------------------------------------------------------------ PARÂMETROS (troque aqui)
72
+ // Tempo: número (segundos da cena), "palavra" ou {palavra, depois, padrao}. UMA palavra, procurada na fala do
73
+ // trecho sem acento e pelo começo; "depois" pula as repetições anteriores; "padrao" vale quando a palavra não
74
+ // aparece (cena aberta sozinha no estúdio, transcrição que mudou).
75
+ const PARAM = {
76
+ termo: "como organizar as tarefas", // o que a barra digita (aqui só o exemplo: troque pelo que a pessoa diz que busca)
77
+ inicio: { palavra: "como", padrao: 0.9 }, // a palavra falada em que a 1ª letra aparece (a digitação vai até a palavra do envio,
78
+ // no máximo uma palavra falada por palavra do termo)
79
+ envio: { palavra: "resultado", padrao: 3.8 }, // a palavra em que a lupa dá o toque e o cartão de resultado entra por baixo da barra; null = logo depois da última letra
80
+ resultado: { titulo: "Guia passo a passo", linha: "um jeito simples de começar" }, // o título vai na faixa de destaque; linha: null = só o título; resultado: null = sem cartão
81
+ chamada: { texto: "Comenta GUIA", em: { palavra: "comenta", padrao: 6 } }, // o selo da chamada: "Comenta [PALAVRA]" é o padrão; "em" = a palavra falada em que ele entra
82
+ // (sem "em", a primeira palavra do texto); pode ser só o texto; null = sem selo
83
+ entra: 0, // quando a barra aparece (fica parada uns "pausa" segundos antes da 1ª letra)
84
+ pausa: 0.6, // tudo parado pelo menos isso depois de cada ação
85
+ legenda: true, // tela cheia: deixa livre a faixa da legenda (a cena usa os 70% de cima da zona segura)
86
+ // Com false a cena usa a zona segura inteira: esconda a legenda no trecho ("esconde_legenda": true)
87
+ fundo: "auto", // "auto" (papel em tela cheia, transparente na moldura), "papel", "#rrggbb" ou null
88
+ area: null, // [x0, y0, x1, y1] em px; "acima_do_rosto" = cartão pequeno acima da cabeça, sem fundo (a pessoa em tela cheia);
89
+ // null = área livre da moldura ou a zona segura
90
+ entrada: 0.2,
91
+ saida: 0.25,
92
+ };
93
+ // ------------------------------------------------------------------ dados do build.py (padrões para abrir sozinha)
94
+ const C = Object.assign({ palavras: [], duracao: 8, largura: 1080, altura: 1920, seguro: [140, 230, 940, 1530], rosto: null, moldura: null,
95
+ cores: { papel: "#fffdfd", linha: "#e6ddf8", base: "#c7b7ef", destaque: "#665080", tinta: "#352c40", verde: "#2e9e5b", vermelho: "#e5484d" } },
96
+ (window.__cenas || {})[ID] || {});
97
+ const DUR = C.duracao, U = Math.min(C.largura, C.altura) / 1080;
98
+ // a raiz precisa do tamanho do vídeo: em 16:9 (ou outro formato) troque data-width e data-height da raiz, senão a cena sai cortada
99
+ const raiz = document.getElementById("k2db-fundo").parentElement;
100
+ if (raiz.offsetWidth && (Math.abs(raiz.offsetWidth - C.largura) > 2 || Math.abs(raiz.offsetHeight - C.altura) > 2))
101
+ console.error(`[${ID}] a raiz da cena tem ${raiz.offsetWidth} x ${raiz.offsetHeight} e o vídeo ${C.largura} x ${C.altura}: troque data-width e data-height da raiz`);
102
+ // ------------------------------------------------------------------ "acima_do_rosto": o cartão acima da cabeça, sem cobrir o rosto
103
+ // area: "acima_do_rosto" vira [seguro.x0, seguro.y0 + 20, seguro.x1, rosto.topo + 40] (sem rosto, a faixa de cima da zona segura,
104
+ // 25% da altura) e o fundo "auto" vira transparente (a pessoa aparece por baixo). O "topo" é uma estimativa com folga (uns 80 a 100 px
105
+ // acima do cabelo de verdade): 40 px abaixo dele o cartão ainda acaba uns 40 a 60 px acima do cabelo e cresce até esse limite.
106
+ // Na moldura o lugar já é a área livre acima do quadro.
107
+ const ZS = C.seguro, ROSTO = C.rosto, ACIMA = PARAM.area === "acima_do_rosto" && !C.moldura;
108
+ if (PARAM.area === "acima_do_rosto") {
109
+ if (!ACIMA) PARAM.area = null;
110
+ else {
111
+ const ya = ZS[1] + 20, yb = ROSTO ? ROSTO.topo + 40 : Math.round(ZS[1] + (ZS[3] - ZS[1]) * 0.25);
112
+ if (yb - ya < 200) console.warn(`[${ID}] não há espaço acima do rosto (só ${Math.max(0, Math.round(yb - ya))} px livres, o cartão pede uns 200): é melhor tela cheia com fundo ou moldura neste trecho. Desenhei o cartão com o que sobra (no mínimo 160 px) na zona segura de cima, o melhor que coube`);
113
+ PARAM.area = [ZS[0], ya, ZS[2], Math.max(yb, ya + 160)];
114
+ if (PARAM.fundo === "auto") PARAM.fundo = null;
115
+ }
116
+ }
117
+ // o retângulo do cartão (x0, y0, x1, y1) encosta no rosto, do topo da cabeça ao queixo, com 20 px de folga? Só vale com o fundo transparente
118
+ // (a pessoa aparece por baixo) e quando a área não é a "acima_do_rosto", que já fica acima da cabeça
119
+ const avisaRosto = (x0, y0, x1, y1) => { if (!fundo && !ACIMA && ROSTO && x0 < ROSTO.x1 + 20 && x1 > ROSTO.x0 - 20 && y0 < ROSTO.y1 + 20 && y1 > Math.min(ROSTO.y0, ROSTO.topo ?? ROSTO.y0) - 20)
120
+ console.warn(`[${ID}] o cartão cobre o rosto: use area: "acima_do_rosto" ou desça/suba a area`); };
121
+ // ------------------------------------------------------------------ ajudantes do kit 2D (iguais em todas as cenas do kit; não precisa mexer)
122
+ const sem = (s) => String(s).normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]/g, "");
123
+ const palavra = (txt, depois, padrao) => { const a = sem(txt), p = a && (C.palavras || []).find((w) => w.t >= depois - 1e-3 && sem(w.texto).startsWith(a)); return p ? p.t : padrao; };
124
+ const momento = (m, padrao) => (typeof m === "number" ? m : typeof m === "string" ? palavra(m, 0, padrao)
125
+ : m && typeof m === "object" ? palavra(m.palavra || "", m.depois == null ? 0 : momento(m.depois, 0), m.padrao == null ? padrao : m.padrao) : padrao);
126
+ // fila: uma ação de cada vez; a próxima espera a anterior terminar e ficar parada PARAM.pausa segundos
127
+ let fimUltima = C.moldura ? Math.max(0, C.moldura.fechada_em || 0) : 0, livre = fimUltima + PARAM.entrada;
128
+ function agenda(quer, dur, colada) {
129
+ const t = Math.max(quer, colada ? fimUltima : livre);
130
+ if (t - quer > 0.4) console.warn(`[${ID}] uma ação entrou ${(t - quer).toFixed(2)} s depois da palavra: afaste as palavras ou diminua "pausa"`);
131
+ fimUltima = t + dur; livre = fimUltima + PARAM.pausa; return t;
132
+ }
133
+ const cor = (n) => C.cores[n] || n;
134
+ const rgb = (h) => { h = String(h).replace("#", ""); if (h.length === 3) h = h.replace(/./g, "$&$&"); return [0, 2, 4].map((i) => parseInt(h.slice(i, i + 2), 16)); };
135
+ const mistura = (a, b, k) => "#" + rgb(a).map((v, i) => Math.round(v + (rgb(b)[i] - v) * k).toString(16).padStart(2, "0")).join("");
136
+ // texto: largura estimada da Montserrat até o peso 900, com folga (a fonte ainda não carregou quando a cena monta), quebra e o maior tamanho que cabe
137
+ const peso = (ch) => { const b = ch.normalize("NFD")[0]; return b === " " ? 0.28 : "MW".includes(b) ? 1 : "mw".includes(b) ? 0.92 : "iljI.,:;'!|".includes(b) ? 0.32 : /[A-Z]/.test(b) ? 0.76 : /[0-9]/.test(b) ? 0.68 : 0.62; };
138
+ const larg = (t, px) => [...String(t)].reduce((s, ch) => s + peso(ch), 0) * px * 1.1;
139
+ const linhas = (t, px, w) => String(t).split(/\s+/).filter(Boolean).reduce((ls, p) => { const u = ls[ls.length - 1]; if (u !== undefined && larg(u + " " + p, px) <= w) ls[ls.length - 1] = u + " " + p; else ls.push(p); return ls; }, []);
140
+ const cabe = (t, w, h, maior, menor, max) => { for (let px = Math.round(maior); px > menor; px--) { const ls = linhas(t, px, w); if (ls.length <= max && ls.every((l) => larg(l, px) <= w) && ls.length * px * 1.16 <= h) return px; } return Math.round(menor); };
141
+ // onde a cena fica: PARAM.area, a área livre da moldura ou a zona segura (sem a faixa da legenda, com PARAM.legenda)
142
+ const A = PARAM.area ? PARAM.area.slice() : C.moldura && C.moldura.area_livre ? C.moldura.area_livre.slice()
143
+ : [C.seguro[0], C.seguro[1], C.seguro[2], PARAM.legenda ? Math.round(C.seguro[1] + (C.seguro[3] - C.seguro[1]) * 0.7) : C.seguro[3]];
144
+ const AW = A[2] - A[0], AH = A[3] - A[1];
145
+ const fundo = PARAM.fundo === "auto" ? (C.moldura ? null : "papel") : PARAM.fundo;
146
+ function pintaFundo(el) {
147
+ if (fundo === "papel" && C.fundo_css) el.style.cssText += ";" + C.fundo_css; // estilo escuro: o fundo do próprio estilo (grade e brilho)
148
+ else if (fundo === "papel") { const g = Math.round(60 * U); Object.assign(el.style, { backgroundColor: C.cores.papel, backgroundSize: `${g}px ${g}px`, backgroundImage: `linear-gradient(${C.cores.linha} 2px,transparent 2px),linear-gradient(90deg,${C.cores.linha} 2px,transparent 2px)` }); }
149
+ else if (fundo) el.style.backgroundColor = fundo;
150
+ }
151
+ const novo = (tag, id, cls, pai, estilo, texto) => { const e = document.createElement(tag); if (id) e.id = id; if (cls) e.className = cls; if (estilo) Object.assign(e.style, estilo); if (texto != null) e.textContent = texto; pai.appendChild(e); return e; };
152
+ const px = (v) => `${v}px`;
153
+ const claro = (h) => { const [r, g, b] = rgb(h); return 0.299 * r + 0.587 * g + 0.114 * b > 150; }; // letra escura em fundo claro
154
+ // letra sobre uma cor: escura em fundo claro, branca em fundo escuro. "escuro" é a tinta do estilo (a letra escura de
155
+ // papelaria e noite) ou, num estilo escuro (tinta clara), o papel
156
+ const escuro = claro(C.cores.tinta) ? C.cores.papel : C.cores.tinta;
157
+ const sobre = (fundo) => (claro(fundo) ? escuro : "#ffffff");
158
+
159
+ // ------------------------------------------------------------------ o texto: palavras e letras, cada letra invisível até a vez dela
160
+ const $ = (id) => document.getElementById(id);
161
+ pintaFundo($("k2db-fundo"));
162
+ const T = String(PARAM.termo || "").normalize("NFC").split(/\s+/).filter(Boolean);
163
+ if (!T.length) { console.error(`[${ID}] o parâmetro "termo" está vazio: escreva o que a pessoa busca`); T.push("?"); }
164
+ const RES = PARAM.resultado && typeof PARAM.resultado === "object" && String(PARAM.resultado.titulo || "").trim() ? PARAM.resultado : null;
165
+ const LIN = RES && String(RES.linha || "").trim() ? String(RES.linha) : "";
166
+ const CH = PARAM.chamada == null ? null : typeof PARAM.chamada === "string" ? { texto: PARAM.chamada } : PARAM.chamada;
167
+ const TCH = CH ? String(CH.texto || "").trim() : "";
168
+ // a escala: a maior em que a pilha inteira (barra com as linhas que o termo faz, cartão de resultado, selo) cabe na área. Em área baixa e larga
169
+ // (o cartão acima da cabeça) tudo usa a largura toda e a letra cresce até a altura que sobra
170
+ const baixa = AH / AW < 0.45, largura = (u) => Math.min(AW - 40 * u, baixa ? Math.max(880 * u, 4.6 * AH) : 880 * u);
171
+ const medidas = (u, nB, n1, n2) => {
172
+ const W = largura(u), P = 20 * u, bt = 68 * u, xt = P + bt + 18 * u, tw = W - xt - P, fs = cabe(PARAM.termo, tw, AH, 54 * u, 24 * u, 2), lh = Math.round(fs * 1.25);
173
+ const Pc = 28 * u, tc = W - 2 * Pc, f1 = RES ? cabe(RES.titulo, tc, AH, 50 * u, 24 * u, 2) : 0, f2 = LIN ? cabe(LIN, tc, AH, 34 * u, 20 * u, 2) : 0;
174
+ const l1 = Math.round(f1 * 1.2), l2 = Math.round(f2 * 1.3), pv = P + Math.max(0, (bt - lh) / 2), Hb = 2 * pv + (nB == null ? linhas(PARAM.termo, fs, tw).length : nB) * lh;
175
+ const faixa = RES ? 2 * 18 * u + (n1 == null ? linhas(RES.titulo, f1, tc).length : n1) * l1 : 0, corpo = LIN ? 2 * 16 * u + (n2 == null ? linhas(LIN, f2, tc).length : n2) * l2 : 0;
176
+ const S = 12 * u, g1 = 22 * u, g2 = 30 * u, hSel = TCH ? 80 * u : 0, Hc = faixa + corpo;
177
+ return { W, P, bt, xt, tw, fs, lh, pv, Hb, Pc, tc, f1, f2, l1, l2, faixa, corpo, Hc, S, g1, g2, hSel,
178
+ total: Hb + S + (RES ? g1 + Hc + S : 0) + (TCH ? g2 + hSel : 0) };
179
+ };
180
+ let k = Math.min(1.35, AW / (800 * U));
181
+ while (k > 0.3 && medidas(U * k).total > AH) k -= 0.02;
182
+ const u = U * k, M0 = medidas(u), bw = Math.max(3, 4 * u), R = Math.min(46 * u, M0.Hb / 2), Rc = 30 * u;
183
+ const { fs, lh, pv, xt, tw, bt, P } = M0, yc = P + bt / 2;
184
+ const tx = $("k2db-texto"), letras = [], falaPalavra = [];
185
+ Object.assign(tx.style, { left: px(xt), top: px(pv), width: px(tw), fontSize: px(fs), lineHeight: px(lh), color: C.cores.tinta });
186
+ T.forEach((w, k) => {
187
+ const pe = novo("span", `k2db-palavra-${k}`, "k2db-palavra", tx, { marginRight: px(0.27 * fs) });
188
+ falaPalavra.push(letras.length);
189
+ [...w].forEach((ch) => letras.push(novo("span", `k2db-letra-${letras.length}`, "k2db-letra", pe, null, ch)));
190
+ });
191
+ // a lupa (neutra até a 1ª letra, depois colorida), o cursor e os anéis do envio
192
+ Object.assign($("k2db-lupa").style, { left: px(P), top: px(yc - bt / 2), width: px(bt), height: px(bt), background: mistura(C.cores.linha, C.cores.papel, 0.35), color: mistura(C.cores.tinta, C.cores.papel, 0.62) });
193
+ Object.assign($("k2db-lupa-cor").style, { background: C.cores.destaque, color: sobre(C.cores.destaque) });
194
+ const aneis = [0, 1, 2].map((i) => { const r = bt / 2 + (8 + 11 * i) * u;
195
+ return novo("div", `k2db-anel-${i}`, "k2db-anel", $("k2db-conteudo"), { left: px(P + bt / 2 - r), top: px(yc - r), width: px(2 * r), height: px(2 * r), border: `${bw}px solid ${C.cores.destaque}` }); });
196
+ const cw = Math.max(3, 4 * u), chh = fs * 1.05, cur = $("k2db-cursor");
197
+ Object.assign(cur.style, { left: px(-cw / 2), width: px(cw), height: px(chh), borderRadius: px(cw / 2), background: C.cores.destaque });
198
+ // o cartão de resultado e o selo da chamada (letra do título sobre a faixa de destaque; o selo é da cor da tinta, com o balão na cor de destaque)
199
+ const tit = $("k2db-titulo"), lin = $("k2db-linha"), sel = $("k2db-selo"), cSel = C.cores.tinta, fsS = 38 * u, icS = 56 * u;
200
+ if (!RES) $("k2db-resultado").style.display = "none";
201
+ else {
202
+ tit.textContent = String(RES.titulo); lin.textContent = LIN;
203
+ Object.assign(tit.style, { width: px(M0.tc), fontSize: px(M0.f1), lineHeight: px(M0.l1), color: sobre(C.cores.destaque) });
204
+ Object.assign(lin.style, { width: px(M0.tc), fontSize: px(M0.f2), lineHeight: px(M0.l2), color: mistura(C.cores.tinta, C.cores.papel, 0.22) });
205
+ $("k2db-faixa").style.background = C.cores.destaque;
206
+ }
207
+ if (!TCH) sel.style.display = "none";
208
+ else { $("k2db-selo-txt").textContent = TCH; Object.assign($("k2db-selo-txt").style, { fontSize: px(fsS), lineHeight: px(M0.hSel), color: sobre(cSel) });
209
+ $("k2db-selo-cor").style.background = cSel; Object.assign($("k2db-selo-ic").style, { left: px(12 * u), top: px((M0.hSel - icS) / 2), width: px(icS), height: px(icS), background: C.cores.destaque, color: sobre(C.cores.destaque) }); }
210
+
211
+ // ------------------------------------------------------------------ quando cada letra aparece (por palavra falada)
212
+ const P0 = C.palavras || [], L0 = T.map((w) => [...w].length), nL = letras.length;
213
+ const tEnv = PARAM.envio == null ? null : momento(PARAM.envio, 3.8);
214
+ const indice = (m, de) => { const a = sem(typeof m === "string" ? m : (m && m.palavra) || ""), dep = m && typeof m === "object" && m.depois != null ? momento(m.depois, 0) : 0;
215
+ return a ? P0.findIndex((w, i) => i >= de && w.t >= dep - 1e-3 && sem(w.texto).startsWith(a)) : -1; };
216
+ let falados = [];
217
+ const i0 = PARAM.inicio == null ? -1 : indice(PARAM.inicio, 0);
218
+ if (i0 >= 0) {
219
+ let i1 = i0; while (i1 + 1 < P0.length && i1 - i0 + 1 < T.length && (tEnv == null || P0[i1 + 1].t < tEnv - 0.2)) i1++;
220
+ falados = P0.slice(i0, i1 + 1).map((w) => ({ t: w.t, fim: Math.max(w.fim == null ? w.t + 0.3 : w.fim, w.t + 0.12) }));
221
+ }
222
+ const iv = []; // [começo, fim] de cada palavra do termo, em segundos da cena
223
+ if (falados.length) {
224
+ // a palavra k do termo cai na palavra falada que a chama; se várias caem na mesma, dividem o intervalo dela
225
+ const m = falados.length, alvo = T.map((_, k) => (T.length === 1 ? 0 : Math.round(k * (m - 1) / (T.length - 1))));
226
+ alvo.forEach((j, k) => { const s = falados[j], g = alvo.filter((v) => v === j).length, r = alvo.slice(0, k).filter((v) => v === j).length;
227
+ iv.push([s.t + (s.fim - s.t) * r / g, s.t + (s.fim - s.t) * (r + 1) / g]); });
228
+ } else {
229
+ // sem fala (cena aberta sozinha ou palavra que não apareceu): reparte pela duração, entre o início e o fim padrão
230
+ const a0 = momento(PARAM.inicio, 0.9), a1 = tEnv == null ? a0 + Math.max(0.8, nL * 0.07) : Math.max(a0 + 0.8, tEnv - 1.0);
231
+ const tot = L0.reduce((s, v) => s + v + 0.7, 0); let acc = 0;
232
+ L0.forEach((v) => { iv.push([a0 + (a1 - a0) * acc / tot, a0 + (a1 - a0) * (acc + v) / tot]); acc += v + 0.7; });
233
+ }
234
+ const tLet = [];
235
+ T.forEach((_, k) => { for (let c = 0; c < L0[k]; c++) tLet.push(iv[k][0] + (iv[k][1] - iv[k][0]) * c / L0[k]); });
236
+
237
+ // ------------------------------------------------------------------ a linha do tempo (montada com a fonte carregada, porque a posição de cada letra e as linhas do texto são medidas no navegador)
238
+ const montar = () => {
239
+ const r0 = tx.getBoundingClientRect(), esc = tx.offsetWidth && r0.width ? r0.width / tx.offsetWidth : 1;
240
+ const med = letras.map((el) => { const r = el.getBoundingClientRect();
241
+ return { x0: (r.left - r0.left) / esc, x1: (r.right - r0.left) / esc, ln: Math.max(0, Math.floor(((r.top + r.bottom) / 2 - r0.top) / esc / lh)) }; });
242
+ // as linhas que o navegador de verdade quebrou (o termo na barra, o título e a linha do resultado) decidem a altura de cada peça
243
+ const nB = Math.max(...med.map((m) => m.ln)) + 1, n1 = RES ? Math.max(1, Math.round(tit.offsetHeight / M0.l1)) : 0, n2 = LIN ? Math.max(1, Math.round(lin.offsetHeight / M0.l2)) : 0;
244
+ const M = medidas(u, nB, n1, n2), { W, Hb, S, g1, g2, hSel, Hc, faixa } = M, yRes = Hb + S + g1, ySel = (RES ? yRes + Hc + S : Hb + S) + g2, Hbl = M.total;
245
+ const Xb = A[0] + (AW - W) / 2, Yb = A[1] + (AH - Hbl) / 2;
246
+ Object.assign($("k2db-bloco").style, { left: px(Xb), top: px(Yb), width: px(W), height: px(Hbl) });
247
+ Object.assign($("k2db-barra").style, { left: 0, top: 0, width: px(W), height: px(Hb + S) });
248
+ Object.assign($("k2db-borda").style, { left: 0, top: 0, width: px(W), height: px(Hb + S), borderRadius: px(R), background: C.cores.base }); // a borda e a sombra dura são a mesma peça
249
+ Object.assign($("k2db-corpo").style, { left: px(bw), top: px(bw), width: px(W - 2 * bw), height: px(Hb - 2 * bw), borderRadius: px(R - bw), background: C.cores.papel });
250
+ if (RES) {
251
+ Object.assign($("k2db-resultado").style, { left: 0, top: px(yRes), width: px(W), height: px(Hc + S) });
252
+ Object.assign($("k2db-res-borda").style, { left: 0, top: 0, width: px(W), height: px(Hc + S), borderRadius: px(Rc), background: C.cores.base });
253
+ Object.assign($("k2db-res-corpo").style, { left: px(bw), top: px(bw), width: px(W - 2 * bw), height: px(Hc - 2 * bw), borderRadius: px(Rc - bw), background: C.cores.papel });
254
+ $("k2db-faixa").style.height = px(faixa - bw);
255
+ Object.assign(tit.style, { left: px(M.Pc - bw), top: px(18 * u - bw) });
256
+ Object.assign(lin.style, { left: px(M.Pc - bw), top: px(faixa - bw + 16 * u) });
257
+ }
258
+ if (TCH) { const ws = 12 * u + icS + 16 * u + $("k2db-selo-txt").offsetWidth + 30 * u;
259
+ Object.assign(sel.style, { left: px((W - ws) / 2), top: px(ySel), width: px(ws), height: px(hSel), borderRadius: px(hSel / 2) });
260
+ Object.assign($("k2db-selo-txt").style, { left: px(12 * u + icS + 16 * u), top: 0 }); }
261
+ avisaRosto(Xb, Yb, Xb + W, Yb + Hbl);
262
+ const tl = gsap.timeline({ paused: true });
263
+ tl.fromTo("#k2db-fundo", { opacity: 0 }, { opacity: 1, duration: Math.max(0.01, PARAM.entrada), ease: "power1.out" }, 0);
264
+ tl.fromTo("#k2db-bloco", { opacity: 0 }, { opacity: 1, duration: 0.01 }, 0);
265
+ const tE = agenda(momento(PARAM.entra, 0), 0.45);
266
+ // nada anima escala no kit: o Chrome guarda o desenho da escala antiga por um tempo e o render deixa de sair igual
267
+ tl.fromTo("#k2db-barra", { opacity: 0, y: 60 * u }, { opacity: 1, y: 0, duration: 0.45, ease: "back.out(1.5)" }, tE);
268
+ // a digitação: uma ação só, da 1ª à última letra
269
+ const q = tLet[0], d = agenda(q, tLet[nL - 1] - q + 0.08) - q, ts = tLet.map((t) => t + d), tU = ts[nL - 1];
270
+ const pos = (i, depois) => ({ x: xt + (depois ? med[i].x1 + 3 * u : med[i].x0 - 3 * u), y: pv + med[i].ln * lh + (lh - chh) / 2 });
271
+ const pisca = (de, ate) => { for (let t = de; t < ate - 0.1; t += 0.9) { tl.set(cur, { opacity: 1 }, t); tl.set(cur, { opacity: 0 }, Math.min(ate - 0.05, t + 0.45)); } };
272
+ tl.set(cur, pos(0, false), 0);
273
+ pisca(tE + 0.3, ts[0] - 0.05);
274
+ letras.forEach((el, i) => {
275
+ tl.fromTo(el, { opacity: 0 }, { opacity: 1, duration: 0.06, ease: "none" }, ts[i]);
276
+ tl.set(cur, { ...pos(i, true), opacity: 1 }, ts[i]);
277
+ const k = falaPalavra.indexOf(i + 1); // a próxima letra é o começo de uma palavra: o cursor já vai para lá (a linha quebra aqui)
278
+ if (k > 0) tl.set(cur, pos(i + 1, false), Math.min(ts[i + 1] - 0.005, Math.max(ts[i] + 0.02, ts[i + 1] - 0.07)));
279
+ });
280
+ tl.fromTo("#k2db-lupa-cor", { opacity: 0 }, { opacity: 1, duration: 0.15, ease: "power1.out" }, ts[0]);
281
+ // o envio: o cursor pisca esperando, a lupa dá um toque e três anéis acendem em sequência (sem escala)
282
+ const tq = tEnv == null ? tU + 0.9 : tEnv, tp = agenda(Math.max(tq - 0.6, tU + 0.25), 0.55, true);
283
+ pisca(tU + 0.15, tp);
284
+ tl.set(cur, { opacity: 0 }, tp + 0.05);
285
+ tl.fromTo("#k2db-lupa", { y: 0 }, { y: 4 * u, duration: 0.09, ease: "power1.out", yoyo: true, repeat: 1 }, tp);
286
+ aneis.forEach((a, i) => {
287
+ tl.fromTo(a, { opacity: 0 }, { opacity: 0.9, duration: 0.1, ease: "power1.out" }, tp + 0.1 * i);
288
+ tl.fromTo(a, { opacity: 0.9 }, { opacity: 0, duration: 0.22, ease: "power1.in", immediateRender: false }, tp + 0.1 * i + 0.1);
289
+ });
290
+ // o cartão de resultado sai de trás da barra (desce abrindo de cima para baixo); depois a faixa de destaque passa como uma cortina e o título e a linha entram
291
+ if (RES) {
292
+ const t = agenda(tq - 0.1, 0.8, true);
293
+ tl.fromTo("#k2db-resultado", { opacity: 0, y: -(g1 + 50 * u), clipPath: "inset(0% 0% 100% 0%)" }, { opacity: 1, y: 0, clipPath: "inset(0% 0% 0% 0%)", duration: 0.5, ease: "power3.out" }, t);
294
+ tl.fromTo("#k2db-faixa", { opacity: 1, clipPath: "inset(0% 100% 0% 0%)" }, { clipPath: "inset(0% 0% 0% 0%)", duration: 0.3, ease: "power2.inOut" }, t + 0.35);
295
+ tl.fromTo(tit, { opacity: 0, x: -14 * u }, { opacity: 1, x: 0, duration: 0.25, ease: "power2.out" }, t + 0.6);
296
+ if (LIN) tl.fromTo(lin, { opacity: 0, y: 10 * u }, { opacity: 1, y: 0, duration: 0.25, ease: "power2.out" }, t + 0.45);
297
+ }
298
+ // o selo da chamada entra na palavra dita (a cor passa como uma cortina)
299
+ if (TCH) {
300
+ const t = agenda(momento(CH.em != null ? CH.em : TCH.split(/\s+/)[0], fimUltima + 1.2) - 0.1, 0.7, true);
301
+ tl.fromTo(sel, { opacity: 0, y: 26 * u }, { opacity: 1, y: 0, duration: 0.4, ease: "back.out(1.7)" }, t);
302
+ tl.fromTo("#k2db-selo-cor", { opacity: 1, clipPath: "inset(0% 100% 0% 0%)" }, { clipPath: "inset(0% 0% 0% 0%)", duration: 0.3, ease: "power2.inOut" }, t + 0.05);
303
+ tl.fromTo("#k2db-selo-ic", { opacity: 0 }, { opacity: 1, duration: 0.15, ease: "power1.out" }, t + 0.2);
304
+ tl.fromTo("#k2db-selo-txt", { opacity: 0, x: -12 * u }, { opacity: 1, x: 0, duration: 0.25, ease: "power2.out" }, t + 0.22);
305
+ }
306
+ if (DUR - PARAM.saida - fimUltima < 0.6) console.warn(`[${ID}] a última ação termina a ${(DUR - PARAM.saida - fimUltima).toFixed(2)} s do fim da cena: deixe o resultado parado pelo menos 0,6 s (estenda o "ate" da cena)`);
307
+ if (PARAM.saida > 0) tl.fromTo(["#k2db-fundo", "#k2db-bloco"], { opacity: 1 }, { opacity: 0, duration: PARAM.saida, ease: "power1.in", immediateRender: false }, Math.max(0, DUR - PARAM.saida));
308
+ window.__timelines["k2d_busca"] = tl;
309
+ };
310
+ // a letra de verdade precisa estar carregada antes de medir; a timeline só é registrada no fim
311
+ (document.fonts && document.fonts.load ? Promise.all([document.fonts.load(`800 ${Math.round(fs)}px Montserrat`), document.fonts.load(`900 ${Math.round(fsS)}px Montserrat`)]) : Promise.resolve()).then(montar, montar);
312
+ })();
313
+ </script>
314
+ </template>
315
+ </body></html>