primocode 10.19.0 → 11.0.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 (116) hide show
  1. package/LICENSES/opencode-MIT.txt +24 -0
  2. package/LICENSES/pptxgenjs-MIT.txt +31 -0
  3. package/README.md +16 -3
  4. package/agentes/acessibilidade.md +10 -0
  5. package/agentes/ambiente.md +10 -0
  6. package/agentes/api.md +10 -0
  7. package/agentes/backend.md +10 -0
  8. package/agentes/copy.md +10 -0
  9. package/agentes/css-layout.md +10 -0
  10. package/agentes/dados.md +10 -0
  11. package/agentes/depuracao.md +10 -0
  12. package/agentes/design.md +10 -0
  13. package/agentes/docs.md +10 -0
  14. package/agentes/frontend.md +10 -0
  15. package/agentes/geral/capacidades/arquivos.json +132 -0
  16. package/agentes/geral/capacidades/codigo.json +132 -0
  17. package/agentes/geral/capacidades/dados.json +126 -0
  18. package/agentes/geral/capacidades/depuracao.json +126 -0
  19. package/agentes/geral/capacidades/escrita.json +126 -0
  20. package/agentes/geral/capacidades/git.json +126 -0
  21. package/agentes/geral/capacidades/pesquisa.json +126 -0
  22. package/agentes/geral/capacidades/seguranca.json +132 -0
  23. package/agentes/geral/capacidades/shell.json +132 -0
  24. package/agentes/geral/capacidades/web.json +126 -0
  25. package/agentes/git-pr.md +10 -0
  26. package/agentes/indice.md +86 -0
  27. package/agentes/nextjs.md +10 -0
  28. package/agentes/node.md +10 -0
  29. package/agentes/performance.md +10 -0
  30. package/agentes/pesquisa.md +10 -0
  31. package/agentes/python.md +10 -0
  32. package/agentes/react.md +10 -0
  33. package/agentes/refatoracao.md +10 -0
  34. package/agentes/revisor.md +10 -0
  35. package/agentes/seguranca.md +10 -0
  36. package/agentes/sql.md +10 -0
  37. package/agentes/testes.md +10 -0
  38. package/agentes/typescript.md +10 -0
  39. package/agentes/ux.md +10 -0
  40. package/bin/primocode.js +65 -8
  41. package/editor/helpers/camera_viva.py +28 -2
  42. package/lib/act.js +79 -10
  43. package/lib/agentes/index.js +146 -0
  44. package/lib/agentes/nucleo.js +18 -0
  45. package/lib/catalogo.js +12 -2
  46. package/lib/cli/review.js +64 -0
  47. package/lib/cli/subcomandos.js +34 -0
  48. package/lib/cli/tentativas.js +107 -0
  49. package/lib/cli/util.js +51 -0
  50. package/lib/comandos-extra.js +4 -2
  51. package/lib/editor-pro-maquina.js +165 -0
  52. package/lib/editor-pro.js +10 -7
  53. package/lib/entregaveis/cli.js +26 -0
  54. package/lib/entregaveis/especialistas.js +50 -0
  55. package/lib/entregaveis/nucleo.js +111 -0
  56. package/lib/entregaveis/slides.js +221 -0
  57. package/lib/estudio/materiais.js +172 -0
  58. package/lib/fundo.js +34 -1
  59. package/lib/kits.js +41 -2
  60. package/lib/nomes.js +6 -0
  61. package/lib/placar.js +173 -0
  62. package/lib/plugin/autoteste.js +99 -0
  63. package/lib/plugin/edicao-visual.js +133 -0
  64. package/lib/plugin/pesquisar.js +92 -0
  65. package/lib/plugin/preview-cli.js +38 -0
  66. package/lib/plugin/preview.js +89 -0
  67. package/lib/plugin/tunel.js +27 -0
  68. package/lib/review.js +134 -0
  69. package/lib/session/compactacao.js +124 -0
  70. package/lib/statusline.js +52 -0
  71. package/lib/tool/diagnostico.js +100 -0
  72. package/lib/tool/perguntar.js +60 -0
  73. package/lib/tool/reparar-entrada.js +66 -0
  74. package/lib/tool/web.js +102 -0
  75. package/lib/tools.js +20 -1
  76. package/lib/ui.js +1 -1
  77. package/materiais/saas-linear/base.ts +84 -0
  78. package/materiais/saas-linear/camera/Camera.tsx +38 -0
  79. package/materiais/saas-linear/fontes/OFL-Inter.txt +93 -0
  80. package/materiais/saas-linear/fontes/OFL-Newsreader.txt +93 -0
  81. package/materiais/saas-linear/fontes/inter-400.woff2 +0 -0
  82. package/materiais/saas-linear/fontes/inter-600.woff2 +0 -0
  83. package/materiais/saas-linear/fontes/newsreader-400.woff2 +0 -0
  84. package/materiais/saas-linear/formas/Agenda.tsx +46 -0
  85. package/materiais/saas-linear/formas/CaixaPrompt.tsx +41 -0
  86. package/materiais/saas-linear/formas/CartoesIntegracao.tsx +57 -0
  87. package/materiais/saas-linear/formas/Cursor.tsx +40 -0
  88. package/materiais/saas-linear/formas/Menu.tsx +46 -0
  89. package/materiais/saas-linear/formas/Monitor.tsx +28 -0
  90. package/materiais/saas-linear/paleta.json +18 -0
  91. package/materiais/saas-linear/pecas.json +590 -0
  92. package/materiais/saas-linear/regras.md +40 -0
  93. package/materiais/saas-linear/sons/Som.tsx +39 -0
  94. package/materiais/saas-linear/sons/assinatura.mp3 +0 -0
  95. package/materiais/saas-linear/sons/clique.mp3 +0 -0
  96. package/materiais/saas-linear/sons/impacto.mp3 +0 -0
  97. package/materiais/saas-linear/sons/pop-cartao.mp3 +0 -0
  98. package/materiais/saas-linear/sons/pop-palavra.mp3 +0 -0
  99. package/materiais/saas-linear/sons/subida.mp3 +0 -0
  100. package/materiais/saas-linear/sons/swoosh.mp3 +0 -0
  101. package/materiais/saas-linear/sons/trilha.mp3 +0 -0
  102. package/materiais/saas-linear/sons/whoosh-entra.mp3 +0 -0
  103. package/materiais/saas-linear/sons/whoosh-sai.mp3 +0 -0
  104. package/materiais/saas-linear/texturas/Fundo.tsx +18 -0
  105. package/materiais/saas-linear/texturas/Particulas.tsx +22 -0
  106. package/materiais/saas-linear/tipografia/FraseSerifa.tsx +25 -0
  107. package/materiais/saas-linear/tipografia/Marca.tsx +20 -0
  108. package/materiais/saas-linear/tipografia/MarcaFinal.tsx +22 -0
  109. package/materiais/saas-linear/tipografia/Saudacao.tsx +27 -0
  110. package/materiais/saas-linear/transicoes/Cena.tsx +38 -0
  111. package/materiais/saas-linear/transicoes/CorteImpacto.tsx +16 -0
  112. package/package.json +3 -1
  113. package/skills/deck-conferido/SKILL.md +29 -0
  114. package/vendor/pptxgenjs/carregar.js +12 -0
  115. package/vendor/pptxgenjs/jszip.min.js +13 -0
  116. package/vendor/pptxgenjs/pptxgen.cjs.js +7370 -0
@@ -0,0 +1,57 @@
1
+ // CartoesIntegracao: uma fileira de cartões de conector (ícone, nome, o que faz, status + chave).
2
+ // Os cartões sobem em sequência com overshoot pequeno. A chave de cada um liga no quadro `ligaEm`
3
+ // (bolinha corre 8 quadros, trilho vira azul, "Desconectado" -> "Conectado", cartão ganha borda quente).
4
+ // Ícones desenhados: 'correio' 'agenda' 'arquivos' 'chat' 'planilha'. Nunca logo de marca real.
5
+ import React from 'react';
6
+ import {useCurrentFrame} from 'remotion';
7
+ import {COR, FONTE_U, SOMBRA, TELA, e3, eb, seg} from '../base';
8
+
9
+ export type Cartao = {nome: string; desc?: string; icone?: string; cor?: string; ligaEm?: number | 'ja'};
10
+ export type CartoesIntegracaoProps = {cartoes: Cartao[]; x?: number; y?: number; largura?: number; inicio?: number; intervalo?: number; foco?: number};
11
+
12
+ const Icone: React.FC<{tipo: string; cor: string}> = ({tipo, cor}) => {
13
+ const s = {stroke: cor, strokeWidth: 2.2, fill: 'none', strokeLinecap: 'round' as const, strokeLinejoin: 'round' as const};
14
+ return <svg width="34" height="34" viewBox="0 0 24 24">
15
+ {tipo === 'correio' && <><rect x="3" y="5" width="18" height="14" rx="2" {...s} /><path d="M3.5 6.5l8.5 6.5 8.5-6.5" {...s} /></>}
16
+ {tipo === 'agenda' && <><rect x="3" y="5" width="18" height="16" rx="2.5" {...s} /><path d="M3 10h18M8 3v4M16 3v4" {...s} /><rect x="7" y="13" width="4" height="4" rx="1" fill={cor} /></>}
17
+ {tipo === 'arquivos' && <><path d="M3 7h6l2 2h10v10H3z" {...s} /><path d="M3 12h18" {...s} /></>}
18
+ {tipo === 'chat' && <path d="M4 5h16v11H9l-5 4z" {...s} />}
19
+ {tipo === 'planilha' && <><rect x="4" y="3" width="16" height="18" rx="2" {...s} /><path d="M4 9h16M4 15h16M10 3v18" {...s} /></>}
20
+ </svg>;
21
+ };
22
+
23
+ export const CartoesIntegracao: React.FC<CartoesIntegracaoProps> = ({cartoes, x = TELA.cx, y = 360, largura = 920, inicio = 0, intervalo = 4, foco}) => {
24
+ const f = useCurrentFrame();
25
+ const gap = 24, n = cartoes.length;
26
+ const w = (largura - gap * (n - 1)) / n;
27
+ return <div style={{position: 'absolute', left: x - largura / 2, top: y, width: largura, display: 'flex', gap, fontFamily: FONTE_U, alignItems: 'center'}}>
28
+ {cartoes.map((c, i) => {
29
+ const t0 = inicio + i * intervalo;
30
+ const p = eb(seg(f, t0, t0 + 16));
31
+ const liga = c.ligaEm === 'ja' ? 1 : c.ligaEm === undefined ? 0 : e3(seg(f, c.ligaEm, c.ligaEm + 8));
32
+ const pulso = typeof c.ligaEm === 'number' ? Math.sin(Math.PI * seg(f, c.ligaEm, c.ligaEm + 10)) : 0;
33
+ const cor = c.cor ?? COR.agenda[i % COR.agenda.length];
34
+ const dest = foco === i ? 1.04 : 1;
35
+ return <div key={i} style={{width: w, background: COR.cartao, borderRadius: 22, padding: '24px 24px 20px', boxShadow: SOMBRA.cartao,
36
+ border: `1.5px solid ${liga > 0.5 ? '#F0D5C8' : COR.linha}`, opacity: seg(f, t0, t0 + 8),
37
+ transform: `translateY(${(1 - p) * 40}px) scale(${(0.96 + 0.04 * p) * (dest + pulso * 0.02)})`}}>
38
+ <div style={{display: 'flex', gap: 16, alignItems: 'center'}}>
39
+ <div style={{width: 58, height: 58, borderRadius: 16, background: `color-mix(in srgb, ${cor} 13%, white)`, display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 'none'}}>
40
+ <Icone tipo={c.icone ?? 'correio'} cor={cor} />
41
+ </div>
42
+ <div style={{minWidth: 0}}>
43
+ <div style={{fontSize: 27, fontWeight: 600, color: COR.tinta, whiteSpace: 'nowrap'}}>{c.nome}</div>
44
+ {c.desc && <div style={{fontSize: 19, color: COR.mute, marginTop: 4, whiteSpace: 'nowrap'}}>{c.desc}</div>}
45
+ </div>
46
+ </div>
47
+ <div style={{height: 1.5, background: COR.linha, margin: '20px 0 16px'}} />
48
+ <div style={{display: 'flex', alignItems: 'center', justifyContent: 'space-between'}}>
49
+ <span style={{fontSize: 19, color: liga > 0.5 ? COR.azul : COR.mute}}>{liga > 0.5 ? 'Conectado' : 'Desconectado'}</span>
50
+ <div style={{width: 64, height: 36, borderRadius: 18, background: `color-mix(in srgb, ${COR.azul} ${Math.round(liga * 100)}%, #DAD5CB)`, position: 'relative'}}>
51
+ <div style={{position: 'absolute', top: 4, left: 4 + liga * 28, width: 28, height: 28, borderRadius: 14, background: '#fff', boxShadow: '0 2px 5px rgba(0,0,0,0.2)'}} />
52
+ </div>
53
+ </div>
54
+ </div>;
55
+ })}
56
+ </div>;
57
+ };
@@ -0,0 +1,40 @@
1
+ // Cursor: a mão que faz as coisas acontecerem. Anda entre `pontos` com curva suave (sem pular),
2
+ // e em cada quadro de `cliques` afunda (escala 0.82) e solta um anel que se abre. Todo clique causa algo:
3
+ // ponha o clique 2 quadros antes do efeito no elemento (chave, menu, botão). Coordenadas em px da tela.
4
+ import React from 'react';
5
+ import {useCurrentFrame} from 'remotion';
6
+ import {COR, e3, seg} from '../base';
7
+
8
+ export type PontoCursor = {em: number; x: number; y: number};
9
+ export type CursorProps = {pontos: PontoCursor[]; cliques?: number[]; inicio?: number; fim?: number; mao?: boolean; tamanho?: number};
10
+
11
+ export const Cursor: React.FC<CursorProps> = ({pontos, cliques = [], inicio, fim, mao = true, tamanho = 46}) => {
12
+ const f = useCurrentFrame();
13
+ const ps = [...pontos].sort((a, b) => a.em - b.em);
14
+ if (!ps.length) return null;
15
+ const ini = inicio ?? ps[0].em - 6;
16
+ let x = ps[0].x, y = ps[0].y;
17
+ for (let i = 0; i < ps.length - 1; i++) if (f >= ps[i].em) {
18
+ const t = e3(seg(f, ps[i].em, ps[i + 1].em));
19
+ x = ps[i].x + (ps[i + 1].x - ps[i].x) * t; y = ps[i].y + (ps[i + 1].y - ps[i].y) * t;
20
+ }
21
+ const op = seg(f, ini, ini + 6) * (fim !== undefined ? 1 - seg(f, fim, fim + 6) : 1);
22
+ let aperto = 1;
23
+ const aneis: React.ReactNode[] = [];
24
+ cliques.forEach((c, i) => {
25
+ aperto -= 0.18 * Math.sin(Math.PI * seg(f, c, c + 7));
26
+ const r = seg(f, c, c + 14);
27
+ if (r > 0 && r < 1) aneis.push(<div key={i} style={{position: 'absolute', left: -28 - r * 20, top: -28 - r * 20, width: 56 + r * 40, height: 56 + r * 40,
28
+ borderRadius: '50%', border: `2.5px solid ${COR.azul}`, opacity: (1 - r) * 0.6}} />);
29
+ });
30
+ if (op <= 0) return null;
31
+ return <div style={{position: 'absolute', left: x, top: y, opacity: op, zIndex: 10}}>
32
+ {aneis}
33
+ <svg width={tamanho} height={tamanho} viewBox="0 0 32 32" style={{position: 'absolute', left: mao ? -12 : -4, top: -4, transform: `scale(${aperto})`, transformOrigin: '30% 10%',
34
+ filter: 'drop-shadow(0 3px 4px rgba(0,0,0,0.25))'}}>
35
+ {mao
36
+ ? <path d="M12 3.5c1.2 0 2 .9 2 2v8.2l.6-.1V11c0-1.1.8-1.9 1.9-1.9s1.9.8 1.9 1.9v2.5l.5-.1c.2-1 .9-1.6 1.8-1.6 1.1 0 1.9.8 1.9 1.9v1.6l.4-.1c.3-.7.9-1.1 1.7-1.1 1 0 1.8.8 1.8 1.9v5.4c0 4.2-3.1 7.1-7.4 7.1h-1.7c-2.3 0-4.1-1-5.4-2.8L6.6 19.4c-.6-.9-.4-2.1.5-2.7.8-.5 1.9-.4 2.5.4l.4.5V5.5c0-1.1.9-2 2-2z" fill="#fff" stroke="#1E1C19" strokeWidth="1.5" strokeLinejoin="round" />
37
+ : <path d="M5 3l20 11.5-8.6 1.9L12 25z" fill="#1E1C19" stroke="#fff" strokeWidth="1.8" strokeLinejoin="round" />}
38
+ </svg>
39
+ </div>;
40
+ };
@@ -0,0 +1,46 @@
1
+ // Menu: o menu que abre do botão "+". Popover branco, cantos 20 px, sombra longa. Abre a partir do canto
2
+ // (escala 0.92 -> 1 + desfoque) e uma faixa de seleção desce pelos itens nos quadros de `passar`.
3
+ // `x`/`y` = canto de cima à esquerda, em px da tela. Ícones: 'clipe' 'pasta' 'luz' 'plug' 'lupa' 'globo' 'pincel'.
4
+ import React from 'react';
5
+ import {useCurrentFrame} from 'remotion';
6
+ import {COR, FONTE_U, SOMBRA, e3, e4, seg} from '../base';
7
+
8
+ export type ItemMenu = {texto: string; icone?: string; seta?: boolean; apagado?: boolean};
9
+ export type MenuProps = {itens: ItemMenu[]; x?: number; y?: number; largura?: number; inicio?: number; passar?: {em: number; item: number}[]; fechar?: number};
10
+
11
+ const ICONES: Record<string, string> = {
12
+ clipe: 'M15 7l-6.5 6.5a2.5 2.5 0 003.5 3.5L18.5 10a4.5 4.5 0 00-6.4-6.4L5.6 10.1a6.5 6.5 0 009.2 9.2L20 14',
13
+ pasta: 'M3 7h6l2 2h10v10H3z',
14
+ luz: 'M9 18h6M10 21h4M12 3a6 6 0 00-3.5 10.9V16h7v-2.1A6 6 0 0012 3z',
15
+ plug: 'M9 3v5M15 3v5M6 8h12v3a6 6 0 01-12 0zM12 17v4',
16
+ lupa: 'M11 4a7 7 0 100 14 7 7 0 000-14zM20 20l-4-4',
17
+ globo: 'M12 3a9 9 0 100 18 9 9 0 000-18zM3 12h18M12 3c3 3 3 15 0 18M12 3c-3 3-3 15 0 18',
18
+ pincel: 'M4 20c2 0 4-1 4-3a2 2 0 00-4 0M8 15l11-11 1 1-11 11',
19
+ };
20
+
21
+ export const Menu: React.FC<MenuProps> = ({itens, x = 330, y = 120, largura = 440, inicio = 0, passar = [], fechar}) => {
22
+ const f = useCurrentFrame();
23
+ const p = e4(seg(f, inicio, inicio + 12));
24
+ const sai = fechar !== undefined ? e4(seg(f, fechar, fechar + 8)) : 0;
25
+ const alturaItem = 62;
26
+ let sel = -1, ySel = 0;
27
+ const ps = [...passar].sort((a, b) => a.em - b.em);
28
+ for (let i = 0; i < ps.length; i++) if (f >= ps[i].em) {
29
+ const ant = i > 0 ? ps[i - 1].item : ps[i].item;
30
+ const t = e3(seg(f, ps[i].em, ps[i].em + 6));
31
+ sel = ps[i].item; ySel = (ant + (ps[i].item - ant) * t) * alturaItem;
32
+ }
33
+ return <div style={{position: 'absolute', left: x, top: y, width: largura, background: COR.cartao, borderRadius: 20, boxShadow: SOMBRA.menu,
34
+ border: `1.5px solid ${COR.linha}`, padding: '12px 10px', fontFamily: FONTE_U, transformOrigin: '10% 100%',
35
+ opacity: p * (1 - sai), transform: `scale(${0.92 + 0.08 * p - 0.04 * sai})`, filter: p < 0.95 ? `blur(${(1 - p) * 6}px)` : undefined}}>
36
+ <div style={{position: 'relative'}}>
37
+ {sel >= 0 && <div style={{position: 'absolute', left: 0, right: 0, top: ySel, height: alturaItem, borderRadius: 12, background: '#F1EEE7'}} />}
38
+ {itens.map((it, i) => <div key={i} style={{position: 'relative', height: alturaItem, display: 'flex', alignItems: 'center', gap: 18, padding: '0 18px',
39
+ fontSize: 25, color: it.apagado ? '#C4BFB4' : COR.tinta, opacity: seg(f, inicio + 3 + i * 1.5, inicio + 9 + i * 1.5)}}>
40
+ <svg width="26" height="26" viewBox="0 0 24 24" style={{flex: 'none'}}><path d={ICONES[it.icone ?? 'clipe'] ?? ICONES.clipe} stroke={it.apagado ? '#C4BFB4' : '#6B665C'} strokeWidth="1.7" fill="none" strokeLinecap="round" strokeLinejoin="round" /></svg>
41
+ <span style={{flex: 1}}>{it.texto}</span>
42
+ {it.seta && <span style={{color: COR.mute, fontSize: 22}}>›</span>}
43
+ </div>)}
44
+ </div>
45
+ </div>;
46
+ };
@@ -0,0 +1,28 @@
1
+ // Monitor: o aparelho que segura a tela do produto. Borda fina escura, pé e base, sombra macia na parede.
2
+ // A tela (1500 x 940) é creme, com uma luz que cai do canto de cima. As cenas vivem dentro dela.
3
+ // `semAparelho`: só a tela, sem borda nem pé (pra quem não quer o monitor).
4
+ import React from 'react';
5
+ import {AbsoluteFill} from 'remotion';
6
+ import {CENTRO_VIDEO, COR, TELA} from '../base';
7
+
8
+ export type MonitorProps = {semAparelho?: boolean; borda?: number; children?: React.ReactNode};
9
+
10
+ export const Monitor: React.FC<MonitorProps> = ({semAparelho = false, borda = 20, children}) => {
11
+ const x0 = CENTRO_VIDEO.x - TELA.w / 2, y0 = CENTRO_VIDEO.y - TELA.h / 2;
12
+ const b = semAparelho ? 0 : borda;
13
+ return <AbsoluteFill>
14
+ {!semAparelho && <>
15
+ <div style={{position: 'absolute', left: CENTRO_VIDEO.x - 34, top: y0 + TELA.h + b - 10, width: 68, height: 300,
16
+ background: 'linear-gradient(90deg, #110F0E, #24211E 45%, #110F0E)'}} />
17
+ <div style={{position: 'absolute', left: CENTRO_VIDEO.x - 260, top: y0 + TELA.h + b + 280, width: 520, height: 46, borderRadius: 14,
18
+ background: 'linear-gradient(180deg, #3A3530, #161412)', boxShadow: '0 30px 60px rgba(0,0,0,0.6)'}} />
19
+ <div style={{position: 'absolute', left: x0 - b, top: y0 - b, width: TELA.w + b * 2, height: TELA.h + b * 2, borderRadius: 18,
20
+ background: COR.moldura, boxShadow: '0 60px 140px rgba(0,0,0,0.55), 0 0 0 2px #2A2724, inset 0 1px 0 rgba(255,255,255,0.08)'}} />
21
+ </>}
22
+ <div style={{position: 'absolute', left: x0, top: y0, width: TELA.w, height: TELA.h, overflow: 'hidden', background: COR.tela,
23
+ borderRadius: semAparelho ? 0 : 4}}>
24
+ {children}
25
+ <AbsoluteFill style={{pointerEvents: 'none', background: 'linear-gradient(165deg, rgba(255,255,255,0.35) 0%, rgba(255,255,255,0) 35%, rgba(60,40,20,0.07) 100%)'}} />
26
+ </div>
27
+ </AbsoluteFill>;
28
+ };
@@ -0,0 +1,18 @@
1
+ {
2
+ "estilo": "saas-linear",
3
+ "cor": {
4
+ "parede": "#3B1E19", "parede2": "#6A3426", "mesa": "#1E1512", "moldura": "#141312",
5
+ "tela": "#F4F1EA", "cartao": "#FFFFFF", "linha": "#E7E2D8", "tinta": "#29261F", "mute": "#8C877C",
6
+ "acento": "#D46F4D", "azul": "#3D7BF0",
7
+ "agenda": ["#E7655A", "#F0A63A", "#3FA86C", "#4B83E6", "#E08A6C"]
8
+ },
9
+ "sombra": {
10
+ "cartao": "0 2px 4px rgba(41,38,31,0.05), 0 18px 40px rgba(41,38,31,0.10)",
11
+ "menu": "0 4px 10px rgba(41,38,31,0.08), 0 30px 70px rgba(41,38,31,0.16)"
12
+ },
13
+ "video": {"largura": 1080, "altura": 1920, "fps": 30, "segura": {"topo": 168, "base": 1330, "lado": 80}},
14
+ "tela": {"w": 1500, "h": 940, "util": {"x0": 290, "x1": 1210}},
15
+ "fontes": {"serifa": "Newsreader 400", "ui": "Inter 400/600"},
16
+ "easing": {"padrao": "e4 = cubic-bezier(.22,1,.36,1)", "suave": "e3 = smoothstep", "overshoot": "eb ~4%"},
17
+ "batida": {"bpm": 100, "quadros": 18}
18
+ }