sagadeck 1.2.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 (233) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +305 -0
  3. package/SKILL.md +148 -0
  4. package/bin/sagadeck.js +511 -0
  5. package/docs/ANDAMENTO.md +1255 -0
  6. package/docs/EXPERIENCIAS.md +39 -0
  7. package/docs/PLANO-EXPERIENCIAS.md +39 -0
  8. package/docs/PLANO-TESTE-AULAS-TECNICAS.md +44 -0
  9. package/docs/PLANO-TESTE-DESINFORMACAO.md +38 -0
  10. package/docs/REFERENCIA.md +1131 -0
  11. package/package.json +59 -0
  12. package/src/adaptive-layouts.js +18 -0
  13. package/src/ai/art-direction.js +8 -0
  14. package/src/ai/commands.js +130 -0
  15. package/src/ai/context.js +245 -0
  16. package/src/ai/deck-ai.js +1135 -0
  17. package/src/ai/directions.js +74 -0
  18. package/src/ai/document-materials.js +49 -0
  19. package/src/ai/document-visuals.js +215 -0
  20. package/src/ai/evaluation.js +31 -0
  21. package/src/ai/ground.js +55 -0
  22. package/src/ai/llm.js +292 -0
  23. package/src/ai/quality.js +51 -0
  24. package/src/ai/relay.js +45 -0
  25. package/src/ai/staged-generation.js +24 -0
  26. package/src/ai/transform.js +858 -0
  27. package/src/ai/usage.js +17 -0
  28. package/src/ai/variety.js +80 -0
  29. package/src/ai/video-frames.js +24 -0
  30. package/src/ai/video-generation.js +126 -0
  31. package/src/ai/web-operation.js +31 -0
  32. package/src/algo-catalog.js +153 -0
  33. package/src/algo-trace.js +139 -0
  34. package/src/api-client.js +514 -0
  35. package/src/api-collections.js +14 -0
  36. package/src/api-demo.js +222 -0
  37. package/src/aspect.js +40 -0
  38. package/src/build.js +323 -0
  39. package/src/carousel.js +56 -0
  40. package/src/chrome.js +65 -0
  41. package/src/code-highlight.js +68 -0
  42. package/src/code-lab-browser.js +22 -0
  43. package/src/code-lab.js +3 -0
  44. package/src/code-language.js +52 -0
  45. package/src/csv.js +74 -0
  46. package/src/decision-lab.js +20 -0
  47. package/src/deck-file.js +119 -0
  48. package/src/diagram/napkin-examples.js +13 -0
  49. package/src/diagram/napkin.js +539 -0
  50. package/src/docx.js +128 -0
  51. package/src/dynamics/git-sim.js +200 -0
  52. package/src/dynamics/layouts.js +112 -0
  53. package/src/elements.js +554 -0
  54. package/src/experiences.js +124 -0
  55. package/src/exploration.js +31 -0
  56. package/src/export/browser.js +53 -0
  57. package/src/export/estudo.js +67 -0
  58. package/src/export/pptx.js +475 -0
  59. package/src/export/roteiro.js +40 -0
  60. package/src/export/shots.js +182 -0
  61. package/src/figures/charts.js +383 -0
  62. package/src/figures/diagrams.js +117 -0
  63. package/src/figures/icons.js +166 -0
  64. package/src/figures/material-pictos.js +20 -0
  65. package/src/figures/pictos.js +255 -0
  66. package/src/figures/points.js +106 -0
  67. package/src/figures/qr.js +29 -0
  68. package/src/figures/ufmap.js +48 -0
  69. package/src/fiscal/autofix.js +377 -0
  70. package/src/fiscal/normalize.js +115 -0
  71. package/src/graph-lab.js +24 -0
  72. package/src/hologram.js +30 -0
  73. package/src/identity.js +86 -0
  74. package/src/import/crop.js +59 -0
  75. package/src/import/index.js +132 -0
  76. package/src/import/legacy-word.js +42 -0
  77. package/src/import/merge.js +45 -0
  78. package/src/import/office.js +123 -0
  79. package/src/import/omml.js +69 -0
  80. package/src/import/pdf-render.js +56 -0
  81. package/src/import/pptx.js +701 -0
  82. package/src/import/xml.js +42 -0
  83. package/src/infographic.js +401 -0
  84. package/src/layouts.js +723 -0
  85. package/src/lessons.js +171 -0
  86. package/src/library.js +399 -0
  87. package/src/markup.js +116 -0
  88. package/src/master.js +214 -0
  89. package/src/mcp/server.js +371 -0
  90. package/src/meeting.js +42 -0
  91. package/src/motion.js +56 -0
  92. package/src/package.js +116 -0
  93. package/src/playground.js +19 -0
  94. package/src/portal-scene.js +20 -0
  95. package/src/preferences.js +61 -0
  96. package/src/protect.js +51 -0
  97. package/src/purpose.js +24 -0
  98. package/src/pytrace.js +1092 -0
  99. package/src/research/duckduckgo.js +73 -0
  100. package/src/research/research.js +202 -0
  101. package/src/research/svg-image.js +17 -0
  102. package/src/research/visuals.js +52 -0
  103. package/src/runtime/api-collection-ui.js +5 -0
  104. package/src/runtime/api-collections.js +28 -0
  105. package/src/runtime/api-core.js +530 -0
  106. package/src/runtime/api-ui.js +966 -0
  107. package/src/runtime/base.css +1774 -0
  108. package/src/runtime/calc.js +139 -0
  109. package/src/runtime/code-lab.js +371 -0
  110. package/src/runtime/decision-lab.js +49 -0
  111. package/src/runtime/diagram.js +341 -0
  112. package/src/runtime/fit.js +268 -0
  113. package/src/runtime/fonts/caveat.css +2 -0
  114. package/src/runtime/fonts/comic-neue.css +3 -0
  115. package/src/runtime/fonts/fredoka.css +2 -0
  116. package/src/runtime/fonts/index.json +10 -0
  117. package/src/runtime/fonts/inter.css +2 -0
  118. package/src/runtime/fonts/patrick-hand.css +2 -0
  119. package/src/runtime/fonts/plus-jakarta-sans.css +2 -0
  120. package/src/runtime/fonts/press-start-2p.css +2 -0
  121. package/src/runtime/fonts/vt323.css +2 -0
  122. package/src/runtime/formula.js +148 -0
  123. package/src/runtime/graph-lab.js +72 -0
  124. package/src/runtime/meeting.css +7 -0
  125. package/src/runtime/meeting.js +10 -0
  126. package/src/runtime/motion.js +15 -0
  127. package/src/runtime/playground.js +11 -0
  128. package/src/runtime/points.js +19 -0
  129. package/src/runtime/portal-scene.css +6 -0
  130. package/src/runtime/portal-scene.js +22 -0
  131. package/src/runtime/runtime.js +827 -0
  132. package/src/runtime/science.js +89 -0
  133. package/src/runtime/skins/aluminio.css +31 -0
  134. package/src/runtime/skins/arcade.css +25 -0
  135. package/src/runtime/skins/aurora.css +17 -0
  136. package/src/runtime/skins/bauhaus.css +19 -0
  137. package/src/runtime/skins/editorial.css +19 -0
  138. package/src/runtime/skins/jornal.css +17 -0
  139. package/src/runtime/skins/manual-noite.css +14 -0
  140. package/src/runtime/skins/manual.css +14 -0
  141. package/src/runtime/skins/noite.css +21 -0
  142. package/src/runtime/skins/oceano.css +17 -0
  143. package/src/runtime/skins/pop.css +23 -0
  144. package/src/runtime/skins/prata.css +18 -0
  145. package/src/runtime/skins/prisma.css +28 -0
  146. package/src/runtime/skins/rabisco.css +18 -0
  147. package/src/runtime/skins/relevo.css +33 -0
  148. package/src/runtime/skins/sinal.css +16 -0
  149. package/src/runtime/skins/terminal.css +23 -0
  150. package/src/runtime/vendor/KATEX-LICENSE.txt +21 -0
  151. package/src/runtime/vendor/MERMAID-LICENSE.txt +21 -0
  152. package/src/runtime/vendor/PLOTLY-LICENSE.txt +21 -0
  153. package/src/runtime/vendor/katex.css +1 -0
  154. package/src/runtime/vendor/mermaid.min.js +3636 -0
  155. package/src/runtime/vendor/plotly.min.js +3620 -0
  156. package/src/runtime/video-player.js +35 -0
  157. package/src/science.js +162 -0
  158. package/src/studio/ai-response.js +36 -0
  159. package/src/studio/api-routes.js +152 -0
  160. package/src/studio/assets/contexto/leia-me.md +12 -0
  161. package/src/studio/assets/contexto/vendas.csv +7 -0
  162. package/src/studio/assets/imagens/colecao-arquitetura.png +0 -0
  163. package/src/studio/assets/imagens/colecao-cor.png +0 -0
  164. package/src/studio/assets/imagens/colecao-equipe.png +0 -0
  165. package/src/studio/assets/imagens/lavanda-cover.jpg +0 -0
  166. package/src/studio/code-routes.js +32 -0
  167. package/src/studio/command-approval.js +48 -0
  168. package/src/studio/demo-decks.js +98 -0
  169. package/src/studio/direction-routes.js +29 -0
  170. package/src/studio/errors.js +65 -0
  171. package/src/studio/exploration-demo.js +17 -0
  172. package/src/studio/exporting.js +105 -0
  173. package/src/studio/generation-report.js +10 -0
  174. package/src/studio/instance.js +48 -0
  175. package/src/studio/layout-samples.js +152 -0
  176. package/src/studio/project.js +371 -0
  177. package/src/studio/public/api-collections.js +28 -0
  178. package/src/studio/public/app.js +5265 -0
  179. package/src/studio/public/art-preview.js +10 -0
  180. package/src/studio/public/calc-fields.js +11 -0
  181. package/src/studio/public/chat-progress.js +47 -0
  182. package/src/studio/public/developer-fields.js +22 -0
  183. package/src/studio/public/explorer.js +443 -0
  184. package/src/studio/public/history.js +58 -0
  185. package/src/studio/public/index.html +867 -0
  186. package/src/studio/public/inspector.js +270 -0
  187. package/src/studio/public/lab-fields.js +5 -0
  188. package/src/studio/public/library.css +275 -0
  189. package/src/studio/public/library.html +55 -0
  190. package/src/studio/public/library.js +471 -0
  191. package/src/studio/public/merge-decks.js +155 -0
  192. package/src/studio/public/review-ui.js +100 -0
  193. package/src/studio/public/screenshot-editor.js +140 -0
  194. package/src/studio/public/share-ui.js +72 -0
  195. package/src/studio/public/slide-clipboard.js +23 -0
  196. package/src/studio/public/slide-form.js +1263 -0
  197. package/src/studio/public/studio-awareness.js +154 -0
  198. package/src/studio/public/studio-next.css +303 -0
  199. package/src/studio/public/style.css +1262 -0
  200. package/src/studio/public/ui-icons.js +2 -0
  201. package/src/studio/public/viewers.js +372 -0
  202. package/src/studio/public/visual-editor.js +326 -0
  203. package/src/studio/public/workspace.js +18 -0
  204. package/src/studio/public-files.js +1 -0
  205. package/src/studio/request-helpers.js +41 -0
  206. package/src/studio/server.js +1583 -0
  207. package/src/studio/share-routes.js +146 -0
  208. package/src/studio/slide-copy-routes.js +26 -0
  209. package/src/studio/slide-copy.js +30 -0
  210. package/src/studio/snapshot.js +160 -0
  211. package/src/studio/style-routes.js +103 -0
  212. package/src/studio/template-collections.js +97 -0
  213. package/src/studio/versions.js +95 -0
  214. package/src/studio/video-download.js +29 -0
  215. package/src/studio/visual-keys.js +52 -0
  216. package/src/studio/workspaces.js +39 -0
  217. package/src/table.js +93 -0
  218. package/src/templates/scaffold.js +372 -0
  219. package/src/themes.js +604 -0
  220. package/src/trace-view.js +450 -0
  221. package/src/uid.js +19 -0
  222. package/src/video-player.js +40 -0
  223. package/src/visual-edits.js +99 -0
  224. package/src/ws.js +134 -0
  225. package/templates/cenario/Texto no cen/303/241rio.yaml" +171 -0
  226. package/templates/cenario/imagens/cidade.svg +30 -0
  227. package/templates/cenario/imagens/pessoa.svg +15 -0
  228. package/templates/cenario-e-ciencia.yaml +47 -0
  229. package/templates/ensaio-api.yaml +160 -0
  230. package/templates/exemplo-alegre.yaml +75 -0
  231. package/templates/exemplo-keynote.yaml +90 -0
  232. package/templates/exemplo-napkin.yaml +139 -0
  233. package/templates/exemplo.yaml +228 -0
@@ -0,0 +1,270 @@
1
+ /* Inspetor de propriedades (como o Object Inspector do Delphi / o painel da direita do Figma).
2
+ Mostra só o que faz sentido para o que está selecionado no slide; Slide e Apresentação ficam sempre à vista.
3
+ Categorias recolhíveis (o que fica aberto é lembrado neste navegador). Toda mudança grava no deck. */
4
+ (function () {
5
+ let host, ctx, pending = false;
6
+ const KEY = "sagadeck.inspectorOpen";
7
+ const openCats = (() => { try { return JSON.parse(localStorage.getItem(KEY) || "{}"); } catch { return {}; } })();
8
+ const saveOpen = () => { try { localStorage.setItem(KEY, JSON.stringify(openCats)); } catch {} };
9
+ const esc = (v) => String(v ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
10
+ const ic = (name) => `<i class="ic" data-ic="${name}"></i>`;
11
+
12
+ // ---------- o que existe para cada tipo de objeto ----------
13
+ const is = {
14
+ text: (els) => els.every((n) => n.classList.contains("t")),
15
+ shape: (els) => els.every((n) => n.classList.contains("shape")),
16
+ drawn: (els) => els.every((n) => n.querySelector(":scope > .shape-svg")),
17
+ image: (els) => els.every((n) => n.classList.contains("fig-img") || n.querySelector(":scope > img")),
18
+ };
19
+ const WEIGHTS = [["", "Do tema"], ["400", "Normal"], ["500", "Médio"], ["600", "Semibold"], ["700", "Negrito"], ["800", "Extra"]];
20
+ const ALIGNS = [["", "Do layout"], ["left", "Esquerda"], ["center", "Centro"], ["right", "Direita"], ["justify", "Justificado"]];
21
+ const SHADOWS = [["", "Nenhuma"], ["suave", "Suave"], ["forte", "Forte"]];
22
+ const ANIMS = [["", "Do tema"], ["fade", "Aparecer"], ["pop", "Pular"], ["left", "Da esquerda"], ["right", "Da direita"], ["down", "De cima"], ["zoom", "Zoom"], ["none", "Sem animação"]];
23
+
24
+ function objectCats(els) {
25
+ const cats = [{ id: "obj-pos", title: "Posição e tamanho", rows: [
26
+ { k: "dx", label: "Mover na horizontal", type: "num", unit: "px" },
27
+ { k: "dy", label: "Mover na vertical", type: "num", unit: "px" },
28
+ { k: "w", label: "Largura", type: "num", unit: "px", min: 20 },
29
+ { k: "h", label: "Altura", type: "num", unit: "px", min: 20 },
30
+ { k: "rotate", label: "Rotação", type: "num", unit: "°", min: -360, max: 360 },
31
+ { k: "z", label: "Camada (frente/trás)", type: "num", step: 1 },
32
+ ] }];
33
+ if (is.text(els)) cats.push({ id: "obj-text", title: "Texto", rows: [
34
+ { k: "size", label: "Tamanho", type: "num", unit: "px", min: 10, max: 500 },
35
+ { k: "color", label: "Cor", type: "color" },
36
+ { k: "weight", label: "Peso", type: "select", options: WEIGHTS, num: true },
37
+ { k: "italic", label: "Itálico", type: "bool" },
38
+ { k: "align", label: "Alinhamento", type: "select", options: ALIGNS },
39
+ { k: "lineHeight", label: "Entrelinha", type: "num", unit: "×", step: 0.05, min: 0.7, max: 3 },
40
+ { k: "letterSpacing", label: "Espaço entre letras", type: "num", unit: "em", step: 0.01, min: -0.1, max: 0.5 },
41
+ { k: "uppercase", label: "Tudo maiúsculo", type: "bool" },
42
+ ] });
43
+ if (is.shape(els)) cats.push({ id: "obj-shape", title: "Preenchimento e contorno", rows: [
44
+ { k: "fill", label: "Preenchimento", type: "color" },
45
+ { k: "stroke", label: "Contorno", type: "color" },
46
+ { k: "strokeWidth", label: "Espessura do contorno", type: "num", unit: "px", min: 0, max: 40 },
47
+ ...(is.drawn(els) ? [] : [{ k: "radius", label: "Cantos arredondados", type: "num", unit: "px", min: 0 }]),
48
+ ] });
49
+ const look = [
50
+ { k: "opacity", label: "Opacidade", type: "range", unit: "%", scale: 100 },
51
+ { k: "shadow", label: "Sombra", type: "select", options: SHADOWS },
52
+ ];
53
+ if (is.image(els)) cats.push({ id: "obj-img", title: "Imagem", rows: [
54
+ { k: "__replace", label: "Arquivo", type: "action", text: "Trocar imagem" },
55
+ { k: "fit", label: "Encaixe", type: "select", options: [["", "Do slide"], ["cover", "Preencher (corta as bordas)"], ["contain", "Inteira"]] },
56
+ { k: "focus", label: "Ponto de foco", type: "select", hint: "Ao preencher, a parte da foto que nunca é cortada", options: [["", "Centro"], ["top", "Em cima"], ["bottom", "Embaixo"], ["left", "Esquerda"], ["right", "Direita"], ["top left", "Em cima, à esquerda"], ["top right", "Em cima, à direita"], ["bottom left", "Embaixo, à esquerda"], ["bottom right", "Embaixo, à direita"]] },
57
+ ] });
58
+ if (is.image(els)) look.push({ k: "radius", label: "Cantos arredondados", type: "num", unit: "px", min: 0 }, { k: "stroke", label: "Borda", type: "color" }, { k: "strokeWidth", label: "Espessura da borda", type: "num", unit: "px", min: 0, max: 40 });
59
+ cats.push({ id: "obj-look", title: "Aparência", rows: look });
60
+ cats.push({ id: "obj-anim", title: "Animação", rows: [
61
+ { k: "step", label: "Aparece no clique", type: "num", step: 1, min: 1, hint: "vazio = já aparece ao entrar no slide" },
62
+ { k: "anim", label: "Entrada", type: "select", options: ANIMS },
63
+ ] });
64
+ cats.push({ id: "obj-adv", title: "Avançado", rows: [
65
+ { k: "hidden", label: "Oculto", type: "bool" },
66
+ { k: "locked", label: "Travado", type: "bool", hint: "O clique no slide atravessa; selecione pela lista de objetos" },
67
+ { k: "__key", label: "Identificador", type: "readonly" },
68
+ { k: "__clear", label: "Ajustes deste objeto", type: "action", text: "Limpar tudo" },
69
+ ] });
70
+ return cats;
71
+ }
72
+
73
+ function slideCats() {
74
+ const S = ctx.state, slide = S.deck?.slides?.[S.currentSlideIndex] || {};
75
+ const opt = (sel) => [...(document.getElementById(sel)?.options || [])].map((o) => [o.value, o.textContent]);
76
+ return [{ id: "slide", title: `Slide ${S.currentSlideIndex + 1}`, target: "slide", rows: [
77
+ { k: "layout", label: "Layout", type: "select", options: ctx.layouts(), noEmpty: true },
78
+ { k: "tone", label: "Tom", type: "select", options: [["", "Do layout"], ...opt("tone-select")] },
79
+ { k: "deco", label: "Textura", type: "select", options: [["", "Do tema"], ...opt("deco-select")] },
80
+ { k: "density", label: "Densidade", type: "select", options: [["", "Padrão"], ["compact", "Compacta"], ["dense", "Mais conteúdo"]] },
81
+ { k: "time", label: "Tempo", type: "num", unit: "min", step: 0.5, min: 0 },
82
+ { k: "transition", label: "Transição", type: "select", options: [["", "Esmaecer"], ["cut", "Corte seco"]] },
83
+ { k: "maxWords", label: "Limite de palavras", type: "num", min: 10, max: 600, step: 10, hint: "vazio = o da apresentação" },
84
+ { k: "footer", label: "Rodapé", type: "select", options: [["", "Automático"], ["true", "Mostrar"], ["false", "Esconder"]], bool3: true },
85
+ { k: "theme", label: "Tema só neste slide", type: "select", options: [["", "O da apresentação"], ...ctx.themes()] },
86
+ { k: "palette", label: "Paleta só neste slide", type: "select", options: [["", "A da apresentação"], ...ctx.palettes()] },
87
+ { k: "bg", label: "Cor de fundo", type: "color" },
88
+ ] }, { id: "deck", title: "Apresentação", target: "deck", rows: [
89
+ { k: "aspect", label: "Proporção do slide", type: "select", hint: "16:9, 4:3, retrato… ou personalizada (L:A). Ao trocar, os layouts se ajustam sozinhos e o que tem posição livre é reescalado.", options: ctx.aspects?.() || [["", "16:9"]] },
90
+ { k: "purpose", label: "Uso do material", type: "select", hint: "Para apresentar: letra grande, pouco texto, para não dar sono. Para estudar depois: o material vai ser enviado e usado como fonte de estudo, então cabe bastante texto no slide.", options: [["", "Para apresentar: letra grande, pouco texto"], ...ctx.purposes()] },
91
+ { k: "maxWords", label: "Limite de palavras por slide", type: "num", min: 10, max: 600, step: 10, hint: "vazio = o do tipo de material" },
92
+ { k: "theme", label: "Tema", type: "select", options: ctx.themes(), noEmpty: true },
93
+ { k: "palette", label: "Paleta", type: "select", options: [["", "A do tema"], ...ctx.palettes()] },
94
+ { k: "markStyle", label: "Estilo do ==destaque==", type: "select", options: [["", "Marca-texto"], ["sublinhado", "Sublinhado"], ["cor", "Só cor"], ["negrito", "Negrito colorido"], ["nenhum", "Sem destaque"]] },
95
+ { k: "motion", label: "Animações", type: "select", options: [["", "Suaves"], ["none", "Sem animação"], ["expressive", "Expressivas"]] },
96
+ { k: "duration", label: "Duração", type: "num", unit: "min", min: 1, max: 600 },
97
+ { k: "author", label: "Autor", type: "text" },
98
+ { k: "date", label: "Data", type: "text", placeholder: "AAAA-MM-DD" },
99
+ { k: "fit.minCodePt", label: "Mínimo do código ao encolher", type: "num", unit: "pt", min: 6, max: 24, hint: "vazio = o das Preferências" },
100
+ { k: "fit.minTextPt", label: "Mínimo do texto ao encolher", type: "num", unit: "pt", min: 6, max: 30 },
101
+ { k: "fit.wrapCode", label: "Quebrar linhas longas de código", type: "select", options: [["", "O das Preferências"], ["true", "Sim"], ["false", "Não"]], bool3: true },
102
+ ] }];
103
+ }
104
+
105
+ // ---------- valores ----------
106
+ const getPath = (o, k) => k.split(".").reduce((v, p) => (v == null ? undefined : v[p]), o);
107
+ // "fit.minCodePt" = o.fit.minCodePt; undefined apaga (e tira o objeto fit se ficou vazio)
108
+ function setPath(o, k, v) {
109
+ const [a, b] = k.split(".");
110
+ if (!b) { if (v === undefined) delete o[a]; else o[a] = v; return; }
111
+ if (v === undefined) { if (o[a]) { delete o[a][b]; if (!Object.keys(o[a]).length) delete o[a]; } return; }
112
+ o[a] = { ...(o[a] && typeof o[a] === "object" ? o[a] : {}), [b]: v };
113
+ }
114
+ const idOf = (catId, k) => `ip-${catId}-${k}`.replace(/[^a-zA-Z0-9_-]/g, "-");
115
+ function objValue(els, k) {
116
+ if (k === "__key") return els.map((n) => n.dataset.vkey).join(", ");
117
+ if (k === "locked") { const v = els.map((n) => n.hasAttribute("data-locked")); return v.every((x) => x === v[0]) ? (v[0] || undefined) : "__varios"; }
118
+ const vals = els.map((n) => window.SagaVisual.edits(n)[k]);
119
+ return vals.every((v) => JSON.stringify(v) === JSON.stringify(vals[0])) ? vals[0] : "__varios";
120
+ }
121
+ function parse(row, raw, el) {
122
+ if (row.type === "bool") return el.checked ? true : undefined;
123
+ if (raw === "" || raw == null) return undefined;
124
+ if (row.bool3) return raw === "true";
125
+ if (row.type === "num" || row.type === "range" || row.num) { let n = Number(raw); if (!Number.isFinite(n)) return undefined; if (row.scale) n /= row.scale; return n; }
126
+ if (row.type === "color") return raw;
127
+ return String(raw);
128
+ }
129
+
130
+ function control(row, value, catId) {
131
+ const id = idOf(catId, row.k);
132
+ const many = value === "__varios", v = many ? undefined : value;
133
+ const shown = row.scale && v != null ? Math.round(v * row.scale) : v;
134
+ const attrs = `id="${id}" data-k="${esc(row.k)}" aria-label="${esc(row.label)}"`;
135
+ switch (row.type) {
136
+ case "bool": return `<input type="checkbox" ${attrs}${v ? " checked" : ""}${many ? ' class="ip-many"' : ""}>`;
137
+ case "select": return `<select ${attrs}>${row.options.map(([ov, ol]) => `<option value="${esc(ov)}"${String(shown ?? "") === String(ov) ? " selected" : ""}>${esc(ol)}</option>`).join("")}${many ? '<option value="__varios" selected>vários</option>' : ""}</select>`;
138
+ case "color": return `<span class="ip-color"><input type="color" ${attrs} value="${/^#[0-9a-f]{6}$/i.test(v || "") ? v : "#888888"}"><span class="ip-hex">${many ? "vários" : v ? esc(v) : "padrão"}</span></span>`;
139
+ case "range": return `<span class="ip-range"><input type="range" min="0" max="100" step="1" ${attrs} value="${shown ?? 100}"><span class="ip-unit">${many ? "vários" : `${shown ?? 100}%`}</span></span>`;
140
+ case "readonly": return `<span class="ip-ro" title="${esc(v)}">${esc(v)}</span>`;
141
+ case "action": return `<button type="button" class="ip-action" ${attrs}>${esc(row.text)}</button>`;
142
+ case "text": return `<input type="text" ${attrs} value="${esc(shown ?? "")}" placeholder="${esc(row.placeholder || "")}">`;
143
+ default: return `<span class="ip-num"><input type="number" ${attrs} value="${shown ?? ""}" placeholder="${many ? "vários" : esc(row.hint ? "—" : "")}"${row.min != null ? ` min="${row.min}"` : ""}${row.max != null ? ` max="${row.max}"` : ""} step="${row.step ?? 1}">${row.unit ? `<span class="ip-unit">${esc(row.unit)}</span>` : ""}</span>`;
144
+ }
145
+ }
146
+
147
+ function catHTML(cat, valueOf) {
148
+ const isOpen = openCats[cat.id] !== false;
149
+ const rows = cat.rows.map((row) => {
150
+ const value = valueOf(row.k);
151
+ const set = value !== undefined && value !== "__varios" && row.type !== "readonly" && row.type !== "action";
152
+ return `<div class="ip-row" data-row="${esc(row.k)}"${row.hint ? ` title="${esc(row.hint)}"` : ""}><label class="ip-l" for="${idOf(cat.id, row.k)}">${esc(row.label)}</label><div class="ip-v">${control(row, value, cat.id)}</div><button type="button" class="ip-reset" data-reset="${esc(row.k)}" title="Voltar ao padrão" aria-label="Voltar ${esc(row.label)} ao padrão"${set ? "" : " hidden"}>${ic("rotate-ccw")}</button></div>`;
153
+ }).join("");
154
+ return `<details class="ip-cat" data-cat="${cat.id}"${isOpen ? " open" : ""}><summary>${esc(cat.title)}</summary><div class="ip-rows">${rows}</div></details>`;
155
+ }
156
+
157
+ // lista de objetos do slide (camadas): selecionar mesmo o que está travado ou escondido, mostrar/ocultar, travar
158
+ const kindOf = (n) => is.text([n]) ? ["Texto", "type"] : is.image([n]) ? ["Imagem", "image"] : is.shape([n]) ? ["Forma", "square"] : n.classList.contains("tbx") ? ["Texto do original", "type"] : n.classList.contains("drw") ? ["Desenho", "shapes"] : ["Objeto", "shapes"];
159
+ function layersHTML(els) {
160
+ const objs = window.SagaVisual?.objects?.() || [];
161
+ if (!objs.length) return "";
162
+ const rows = objs.map((n, i) => {
163
+ const [kind, icon] = kindOf(n);
164
+ const name = n.textContent.replace(/\s+/g, " ").trim().slice(0, 48) || kind;
165
+ const hidden = !!window.SagaVisual.edits(n).hidden, lk = n.hasAttribute("data-locked");
166
+ return `<div class="ip-layer${els.includes(n) ? " sel" : ""}${hidden ? " off" : ""}" data-layer="${i}" title="${esc(kind)}">${ic(icon)}<span class="ip-layer-name">${esc(name)}</span>`
167
+ + `<button type="button" data-layer-eye aria-label="${hidden ? "Mostrar" : "Ocultar"}" title="${hidden ? "Mostrar" : "Ocultar"}">${ic(hidden ? "eye-off" : "eye")}</button>`
168
+ + `<button type="button" data-layer-lock class="${lk ? "on" : ""}" aria-label="${lk ? "Destravar" : "Travar"}" title="${lk ? "Destravar" : "Travar (o clique no slide atravessa)"}">${ic(lk ? "lock" : "lock-open")}</button></div>`;
169
+ }).join("");
170
+ return `<section class="ip-sec" data-sec="layers"><details class="ip-cat" data-cat="layers"${openCats.layers !== false ? " open" : ""}><summary>Objetos do slide (${objs.length})</summary><div class="ip-layers">${rows}</div></details></section>`;
171
+ }
172
+ function bindLayers() {
173
+ const objs = window.SagaVisual?.objects?.() || [];
174
+ host.querySelectorAll(".ip-layer").forEach((row) => {
175
+ const n = objs[+row.dataset.layer];
176
+ if (!n) return;
177
+ row.onclick = (e) => { if (!e.target.closest("button")) window.SagaVisual.select([n]); };
178
+ row.querySelector("[data-layer-eye]").onclick = () => window.SagaVisual.setPropOn([n], "hidden", window.SagaVisual.edits(n).hidden ? null : true);
179
+ row.querySelector("[data-layer-lock]").onclick = () => window.SagaVisual.setPropOn([n], "locked", n.hasAttribute("data-locked") ? false : true);
180
+ });
181
+ }
182
+
183
+ function render() {
184
+ if (!host || !ctx?.state?.deck) return;
185
+ if (host.contains(document.activeElement) && document.activeElement.matches("input[type=number], input[type=text]")) { pending = true; return; } // digitando: não redesenha
186
+ pending = false;
187
+ const els = window.SagaVisual?.selection() || [];
188
+ const S = ctx.state, slide = S.deck.slides[S.currentSlideIndex] || {};
189
+ let html = "";
190
+ if (els.length) {
191
+ const kind = is.text(els) ? "Texto" : is.shape(els) ? "Forma" : is.image(els) ? "Imagem" : "Objeto";
192
+ const name = els.length > 1 ? `${els.length} objetos` : `${kind}${els[0].textContent.trim() ? `: “${els[0].textContent.trim().slice(0, 28)}”` : ""}`;
193
+ html += `<section class="ip-sec" data-sec="obj"><h4>${ic(kind === "Texto" ? "type" : kind === "Forma" ? "square" : kind === "Imagem" ? "image" : "shapes")}<span>${esc(name)}</span></h4>${objectCats(els).map((c) => catHTML(c, (k) => objValue(els, k))).join("")}</section>`;
194
+ }
195
+ html += layersHTML(els);
196
+ // o conteúdo do slide (itens do carrossel, fotos, destaques do screenshot, dados do gráfico…) mora no Formatar
197
+ html += `<button type="button" class="ip-content" data-open-content>${ic("pencil")}<span>Editar o conteúdo do slide</span></button>`;
198
+ const [sc, dc] = slideCats();
199
+ html += `<section class="ip-sec" data-sec="slide">${catHTML(sc, (k) => { const v = getPath(slide, k); return v === undefined ? undefined : typeof v === "boolean" ? String(v) : v; })}</section>`;
200
+ html += `<section class="ip-sec" data-sec="deck">${catHTML(dc, (k) => { const v = getPath(S.deck, k); return v === undefined ? undefined : typeof v === "boolean" ? String(v) : v; })}</section>`;
201
+ host.innerHTML = html;
202
+ ctx.hydrate(host);
203
+ bind(els);
204
+ bindLayers();
205
+ }
206
+
207
+ function rowOf(k, secId) {
208
+ const cats = secId === "obj" ? objectCats(window.SagaVisual.selection()) : slideCats();
209
+ for (const c of cats) { const r = c.rows.find((x) => x.k === k); if (r && (secId === "obj" || c.target === secId)) return r; }
210
+ return null;
211
+ }
212
+
213
+ function apply(sec, row, value) {
214
+ if (sec === "obj") {
215
+ if (row.k === "__clear") { window.SagaVisual.clearEdits(); return; }
216
+ if (row.k === "__replace") { window.SagaVisual.replaceImage(); return; }
217
+ if (row.k === "locked") { window.SagaVisual.setProp("locked", value ? true : false); return; } // decoração do original vem travada: destravar grava false
218
+ window.SagaVisual.setProp(row.k, value ?? null); // grava, com Desfazer, em todos os selecionados
219
+ return;
220
+ }
221
+ const S = ctx.state;
222
+ if (sec === "slide") {
223
+ const slide = S.deck.slides[S.currentSlideIndex];
224
+ if (row.k === "layout") return ctx.changeLayout(value);
225
+ if (row.k === "theme" || row.k === "palette") return value === undefined ? ctx.applyLook(row.k, slide[row.k], "reset") : ctx.applyLook(row.k, value, "slide");
226
+ setPath(slide, row.k, row.k === "bg" && value ? value.replace("#", "") : value);
227
+ } else {
228
+ if (row.k === "aspect") {
229
+ const v = value === "__custom" ? window.prompt("Proporção personalizada (largura:altura), ex.: 5:4 ou 1200:900", "5:4") : value;
230
+ if (v == null) return render();
231
+ return ctx.changeAspect(v || "16:9");
232
+ }
233
+ if (row.k === "theme") return ctx.applyLook("theme", value, "all");
234
+ if (row.k === "palette") return ctx.applyLook("palette", value ?? "tema", "all");
235
+ setPath(S.deck, row.k, value);
236
+ }
237
+ ctx.commit();
238
+ }
239
+
240
+ function bind() {
241
+ host.querySelector("[data-open-content]")?.addEventListener("click", () => ctx.openContent?.());
242
+ // grava já no clique (o evento toggle chega depois, e um recarregamento logo em seguida o perderia)
243
+ host.querySelectorAll("details.ip-cat").forEach((d) => {
244
+ d.querySelector(":scope > summary").addEventListener("click", () => { openCats[d.dataset.cat] = !d.open; saveOpen(); });
245
+ d.addEventListener("toggle", () => { openCats[d.dataset.cat] = d.open; saveOpen(); });
246
+ });
247
+ host.querySelectorAll("[data-k]").forEach((el) => {
248
+ const sec = el.closest(".ip-sec").dataset.sec, row = rowOf(el.dataset.k, sec);
249
+ if (!row) return;
250
+ if (row.type === "action") { el.onclick = () => apply(sec, row); return; }
251
+ if (row.type === "range") el.addEventListener("input", () => { el.nextElementSibling.textContent = `${el.value}%`; });
252
+ el.addEventListener("change", () => { if (el.value === "__varios") return; apply(sec, row, parse(row, el.value, el)); });
253
+ if (el.matches("input[type=number], input[type=text]")) {
254
+ el.addEventListener("keydown", (e) => { if (e.key === "Enter") { e.preventDefault(); el.blur(); } });
255
+ el.addEventListener("blur", () => { if (pending) setTimeout(render, 0); });
256
+ }
257
+ });
258
+ host.querySelectorAll("[data-reset]").forEach((b) => {
259
+ const sec = b.closest(".ip-sec").dataset.sec, row = rowOf(b.dataset.reset, sec);
260
+ b.onclick = () => row && apply(sec, row, undefined);
261
+ });
262
+ }
263
+
264
+ function setup(container, context) {
265
+ host = container; ctx = context;
266
+ window.SagaVisual?.onSelect((els, why) => { ctx.onSelection?.(els, why); render(); });
267
+ render();
268
+ }
269
+ window.SagaInspector = { setup, refresh: render };
270
+ })();
@@ -0,0 +1,5 @@
1
+ // Formulários dos laboratórios interativos.
2
+ window.SagaLabFields = {
3
+ codelab: (f, obj) => [f.text('title','Título'),f.select('engine','Execução',[['trace','Python de aula · passo a passo offline'],['python','Python nativo · pacotes reais'],['javascript','JavaScript nativo · Node.js']],{empty:false,default:'trace'}),f.area('program','Programa',{mono:true,rows:14}),f.text('call','Chamada / entrada',{mono:true}),f.area('explanation','Desafio para a turma'),f.num('maxSteps','Máximo de passos')],
4
+ graphlab: (f, obj) => [f.text('title','Título'),f.list('nodes','Nós',obj([f.text('id','Id único'),f.text('label','Nome'),f.text('icon','Ícone Lucide'),f.num('x','Posição horizontal'),f.num('y','Posição vertical')]),{newItem:()=>({id:'novo',label:'Novo nó',icon:'map-pin'})}),f.list('edges','Ligações',obj([f.text('from','Origem (id)'),f.text('to','Destino (id)'),f.num('weight','Custo'),f.text('label','Nome da ligação')]),{newItem:()=>({from:'',to:'',weight:1})}),f.bool('directed','Ligações com direção'),f.text('start','Origem inicial'),f.text('goal','Destino inicial'),f.area('explanation','Convite ao experimento')]
5
+ };
@@ -0,0 +1,275 @@
1
+ /* O estúdio usa uma superfície independente dos temas de apresentação. */
2
+ :root { color-scheme: light; --canvas-bg: #f7f6f2; --chrome: #fff; --chrome-2: #f1f0eb; --text: #23232b; --text-2: #61616b; --text-3: #7f7f89; --ui-line: #e7e6e0; --ui-line-strong: #d8d7d0; --brand: #634ce9; --brand-fill: #634ce9; --brand-fill-hover: #5039d1; --on-brand: white; --hover: #efeee9; --selected: #eae7fb; --err: #ba3944; --toast-bg: #25242d; --toast-text: white; --shadow-1: 0 2px 8px #25232c08; --shadow-2: 0 12px 28px #25232c16; --shadow-3: 0 18px 64px #18172026; }
3
+ :root[data-theme=dark] { color-scheme: dark; --canvas-bg: #19191e; --chrome: #212127; --chrome-2: #29292f; --text: #f3f2f6; --text-2: #b8b6c3; --text-3: #95929f; --ui-line: #333239; --ui-line-strong: #47454f; --brand: #ac9aff; --brand-fill: #7861ef; --brand-fill-hover: #8d76ff; --hover: #2c2a34; --selected: #39304f; }
4
+ * { box-sizing: border-box; }
5
+ body { margin: 0; font-family: 'Segoe UI', Arial, sans-serif; color: var(--text); -webkit-font-smoothing: antialiased; }
6
+ button, input, select, textarea { font: inherit; }
7
+ button, a { -webkit-tap-highlight-color: transparent; }
8
+ button { cursor: pointer; }
9
+ button:disabled { opacity: .55; cursor: wait; }
10
+ a { color: inherit; }
11
+ [hidden] { display: none !important; }
12
+ :focus-visible { outline: 3px solid var(--brand); outline-offset: 4px; }
13
+ .ic { display: inline-flex; width: 18px; height: 18px; flex: none; vertical-align: middle; }
14
+ .ic svg { width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
15
+ body.lib { display: grid; grid-template-columns: 218px minmax(0, 1fr); grid-template-rows: 84px minmax(0, 1fr); height: 100vh; height: 100dvh; overflow: hidden; background: var(--canvas-bg); font-size: 14px; }
16
+ .skip-link { position: fixed; top: -80px; left: 20px; z-index: 100; padding: 12px; background: var(--chrome); border-radius: 8px; }
17
+ .skip-link:focus { top: 15px; }
18
+ .lib-brand { display: flex; align-items: center; gap: 11px; padding: 0 24px; border-right: 1px solid var(--ui-line); text-decoration: none; font-size: 23px; font-weight: 720; letter-spacing: -.8px; }
19
+ .brand-light { font-weight: 420; }
20
+ .lib-brand small { display: block; margin-top: 3px; color: var(--text-3); font-size: 8px; font-weight: 650; letter-spacing: 2px; }
21
+ .lib-logo { width: 33px; height: 37px; flex: none; display: grid; place-items: center; color: var(--brand); }
22
+ .lib-logo svg { width: 100%; height: 100%; } /* logotipo: um deck (dois slides) com o "apresentar" no da frente */
23
+ .lib-top { display: flex; align-items: center; gap: 13px; padding: 0 38px; border-bottom: 1px solid var(--ui-line); }
24
+ .breadcrumb { display: flex; gap: 12px; color: var(--text-3); font-size: 12px; white-space: nowrap; }
25
+ .breadcrumb b { font-weight: 500; color: var(--text-2); }
26
+ .breadcrumb span { color: var(--ui-line-strong); }
27
+ .spacer { flex: 1; }
28
+ .lib-search { display: flex; align-items: center; gap: 9px; width: 228px; height: 37px; color: var(--text-3); padding: 0 11px; background: transparent; border: 1px solid var(--ui-line); border-radius: 8px; }
29
+ .lib-search input { background: none; color: var(--text); border: 0; outline: 0; width: 100%; min-width: 0; font-size: 11px; }
30
+ .lib-search kbd { border: 1px solid var(--ui-line); border-radius: 3px; font-size: 10px; padding: 0 4px; }
31
+ .lib-btn { display: inline-flex; justify-content: center; align-items: center; gap: 8px; min-height: 38px; padding: 0 15px; border-radius: 8px; border: 1px solid transparent; font-size: 12px; font-weight: 600; text-decoration: none; transition: background .15s, transform .15s; }
32
+ .lib-btn.primary { background: var(--brand-fill); color: var(--on-brand); box-shadow: 0 3px 8px #634ce916; }
33
+ .lib-btn.primary:hover { background: var(--brand-fill-hover); transform: translateY(-1px); }
34
+ .lib-btn.ghost { background: transparent; border-color: var(--ui-line-strong); color: var(--text); }
35
+ .lib-btn.ghost:hover { background: var(--hover); }
36
+ .lib-btn.danger { color: var(--err); }
37
+ .lib-btn.large { min-height: 44px; padding: 0 18px; }
38
+ .lib-icon-btn { width: 34px; height: 34px; border-radius: 8px; display: grid; place-items: center; color: var(--text-3); background: none; border: 0; cursor: pointer; }
39
+ .lib-icon-btn:hover { background: var(--hover); }
40
+ .lib-user { display: flex; align-items: center; gap: 8px; font-size: 12px; }
41
+ .lib-avatar { display: grid; place-items: center; width: 30px; height: 30px; border-radius: 50%; background: var(--selected); color: var(--brand); text-transform: uppercase; font-weight: 700; }
42
+ .lib-body { display: contents; }
43
+ .lib-side { min-height: 0; display: flex; flex-direction: column; padding: 14px 14px 18px; border-right: 1px solid var(--ui-line); overflow-y: auto; }
44
+ #side { display: flex; flex-direction: column; gap: 5px; flex: 1; min-height: 360px; }
45
+ .nav { display: flex; align-items: center; gap: 10px; min-height: 40px; flex: none; padding: 0 13px; border-radius: 8px; color: var(--text-2); width: 100%; text-align: left; background: none; border: 0; font-size: 12px; }
46
+ .nav:hover { background: var(--hover); }
47
+ .nav.active { background: var(--selected); color: var(--brand); font-weight: 600; }
48
+ .nav .count { margin-left: auto; font-size: 11px; color: var(--text-3); font-variant-numeric: tabular-nums; }
49
+ .nav .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
50
+ .nav.drop { outline: 2px dashed var(--brand); outline-offset: -2px; background: var(--selected); }
51
+ .dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; flex: none; margin: 0 5px; }
52
+ .side-title { display: flex; align-items: center; justify-content: space-between; margin: 28px 13px 8px; font-size: 9px; font-weight: 650; letter-spacing: 1.5px; color: var(--text-3); }
53
+ .side-title button { color: var(--text-3); width: 24px; height: 24px; border-radius: 4px; display: grid; place-items: center; background: none; border: 0; }
54
+ .side-title button:hover { background: var(--hover); color: var(--text); }
55
+ .lib-side .grow { flex: 1; min-height: 32px; }
56
+ .side-foot { border-top: 1px solid var(--ui-line); padding-top: 12px; }
57
+ .where { font-size: 10px; color: var(--text-3); padding: 12px 12px 0; line-height: 1.6; word-break: break-all; }
58
+ .where summary { display: flex; align-items: center; gap: 7px; list-style: none; cursor: pointer; word-break: normal; }
59
+ .where summary::before { content: ''; width: 5px; height: 5px; border-radius: 100%; background: #63a487; }
60
+ .where code { display: block; margin-top: 7px; font-size: 9px; }
61
+ .ai-settings { margin: 12px 7px 0; font-size: 10px; }
62
+ .studio-note { margin: 16px 11px 22px; font-size: 11px; line-height: 1.6; color: var(--text-3); }
63
+ .studio-note strong { display: block; color: var(--text-2); font-size: 12px; margin-bottom: 4px; font-weight: 500; }
64
+ .lib-main { overflow-y: auto; min-width: 0; padding: 36px 44px 64px; scroll-behavior: smooth; outline: none; }
65
+ .lib-main > * { max-width: 1360px; margin-left: auto; margin-right: auto; }
66
+ .hero { position: relative; display: grid; grid-template-columns: 1.1fr .9fr; align-items: center; gap: 36px; padding: 8px 0 40px; }
67
+ .eyebrow { font-size: 9px; font-weight: 650; letter-spacing: 2px; color: var(--text-3); display: flex; align-items: center; gap: 7px; margin-bottom: 17px; }
68
+ .eyebrow .ic { width: 13px; height: 13px; color: var(--brand); }
69
+ .hero h1 { margin: 0; font-size: clamp(36px, 3.9vw, 60px); line-height: 1.1; letter-spacing: -2.4px; font-weight: 570; max-width: 640px; }
70
+ .hero h1 em { color: var(--brand); font-family: Georgia, serif; font-weight: 400; }
71
+ .hero p { max-width: 380px; color: var(--text-2); font-size: 12px; line-height: 1.8; margin: 20px 0; }
72
+ .hero-actions { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
73
+ .text-button { display: inline-flex; align-items: center; gap: 7px; padding: 8px 0; color: var(--text-2); background: none; border: 0; font-size: 11px; text-decoration: none; white-space: nowrap; }
74
+ .text-button:hover { color: var(--brand); }
75
+ .text-button .ic { width: 14px; height: 14px; }
76
+ .hero-stage { position: relative; min-height: 280px; height: 100%; display: flex; align-items: center; justify-content: center; perspective: 900px; }
77
+ .stage-orbit { position: absolute; width: 240px; height: 240px; border: 1px solid var(--ui-line); border-radius: 100%; transform: rotate(-28deg) scaleX(1.45); }
78
+ .stage-orbit::after { content: ''; position: absolute; inset: 30px; border: 1px solid var(--ui-line); border-radius: 100%; }
79
+ .mini-slide { position: absolute; border-radius: 6px; box-shadow: 0 20px 42px #302c4025; overflow: hidden; }
80
+ .mini-beige { width: 235px; height: 150px; left: -3%; top: 18px; padding: 24px; color: #222629; background: #e5ddce; transform: rotate(-11deg); }
81
+ .mini-beige small { font-size: 7px; letter-spacing: 2px; }
82
+ .mini-beige b { display: block; font-family: Georgia, serif; font-size: 31px; line-height: 1.07; font-weight: 400; margin-top: 18px; }
83
+ .mini-beige::after { content: ''; width: 100px; height: 100px; background: #e87a57; position: absolute; right: -33px; bottom: -26px; border-radius: 50%; }
84
+ .mini-pop { width: 198px; height: 131px; right: 0; bottom: 5px; background: #d9ea88; transform: rotate(10deg); color: #273322; padding: 21px; }
85
+ .mini-pop b { font-size: 31px; letter-spacing: -1.5px; line-height: 1; }
86
+ .mini-pop span { font-family: Georgia, serif; font-style: italic; }
87
+ .mini-pop .star { position: absolute; font-size: 86px; bottom: -25px; right: -12px; color: #5b69bd; line-height: 1; }
88
+ .mini-dark { width: 285px; height: 182px; background: #191722; color: #fff; transform: rotate(-1deg); padding: 27px; z-index: 1; }
89
+ .mini-dark small { font-size: 6px; letter-spacing: 2px; color: #c1b8db; position: relative; z-index: 1; }
90
+ .mini-dark b { display: block; font-size: 37px; letter-spacing: -2px; line-height: 1; margin-top: 19px; position: relative; z-index: 1; font-weight: 550; }
91
+ .mini-dark b em { color: #c3affc; font-family: Georgia, serif; font-weight: 400; }
92
+ .mini-dark::before { content: ''; position: absolute; width: 178px; height: 178px; right: -38px; top: 5px; border-radius: 50%; background: radial-gradient(circle at 31% 29%, #ceb3ff 0%, #9071e1 17%, #5d41a0 33%, #251b39 65%); box-shadow: -8px 0 25px #9673e54a; }
93
+ .stage-label { position: absolute; z-index: 2; right: 14%; bottom: -5px; background: var(--chrome); border: 1px solid var(--ui-line); border-radius: 20px; padding: 9px 14px; box-shadow: var(--shadow-1); font-size: 9px; display: flex; gap: 7px; align-items: center; transform: rotate(-4deg); }
94
+ .stage-label .ic { width: 13px; height: 13px; color: var(--brand); }
95
+ .section-head { display: flex; align-items: center; justify-content: space-between; gap: 20px; margin-bottom: 19px; }
96
+ .section-head h2 { font-size: 20px; font-weight: 600; letter-spacing: -.55px; margin: 0; }
97
+ .section-head p { font-size: 11px; color: var(--text-3); margin: 7px 0 0; }
98
+ .experiences-section { border-top: 1px solid var(--ui-line); padding-top: 27px; padding-bottom: 34px; }
99
+ .experience-filters { display: flex; gap: 5px; flex-wrap: wrap; margin-bottom: 20px; }
100
+ .experience-filter { border: 1px solid transparent; background: none; border-radius: 6px; color: var(--text-2); font-size: 10px; padding: 7px 12px; }
101
+ .experience-filter:hover { background: var(--hover); }
102
+ .experience-filter.active { background: var(--text); color: var(--canvas-bg); }
103
+ .experiences-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px; }
104
+ .experience-card { position: relative; text-align: left; padding: 0; background: none; border: 0; color: var(--text); border-radius: 8px; min-width: 0; }
105
+ .experience-art { position: relative; display: block; aspect-ratio: 16 / 9; border-radius: 8px; overflow: hidden; padding: 22px; transition: transform .25s, box-shadow .25s; container-type: inline-size; }
106
+ .experience-card:hover .experience-art { transform: translateY(-4px); box-shadow: var(--shadow-2); }
107
+ .experience-art small { display: block; position: relative; z-index: 1; font-size: clamp(6px, 2.3cqw, 9px); letter-spacing: 1.4px; }
108
+ .experience-art b { display: block; position: relative; z-index: 1; font-size: clamp(20px, 11cqw, 42px); line-height: 1.07; letter-spacing: -.9px; font-weight: 550; margin-top: 17px; }
109
+ .art-executivo { background: #111419; color: #edebe6; }
110
+ .art-executivo small { color: #c2a773; }
111
+ .art-executivo b { max-width: 70%; font-size: 10cqw; }
112
+ .art-executivo em { color: #e4b660; font-weight: 400; font-family: Georgia, serif; }
113
+ .executive-bars { position: absolute; right: 20px; bottom: 20px; display: flex; gap: 4px; align-items: flex-end; height: 55%; width: 22%; }
114
+ .executive-bars i { flex: 1; height: 44%; background: #837759; border-top: 1px solid #e4b660; }
115
+ .executive-bars i:nth-child(2) { height: 67%; background: #ad9361; }
116
+ .executive-bars i:nth-child(3) { height: 100%; background: #e4b660; }
117
+ .art-editorial { background: #eee9df; color: #20211c; }
118
+ .art-editorial b { font-family: Georgia, serif; max-width: 75%; font-weight: 400; font-size: 11cqw; margin-top: 18px; }
119
+ .art-editorial em { font-weight: 400; color: #cc4937; }
120
+ .editorial-mark { position: absolute; width: 37%; aspect-ratio: 1; right: -6%; bottom: -10%; border-radius: 100%; background: #d56346; }
121
+ .editorial-mark::before { content: ''; position: absolute; width: 100%; height: 100%; left: -25%; top: -30%; border: 1px solid #353a2a; border-radius: 100%; }
122
+ .art-palco { background: #10131d; color: #f4f0fa; }
123
+ .art-palco b { font-size: 12cqw; font-weight: 650; max-width: 80%; }
124
+ .art-palco b em { color: #a2eaff; font-weight: 500; font-family: Georgia, serif; }
125
+ .stage-sphere { position: absolute; width: 69%; aspect-ratio: 1; border-radius: 100%; right: -13%; top: -2%; background: radial-gradient(circle at 26% 24%, #92f2f1, #489ad3 20%, #6959ba 36%, #261e56 55%, #181326 70%); box-shadow: -9px 0 35px #52cbfb29; }
126
+ .art-pop { background: #e6dfc9; color: #202622; }
127
+ .art-pop small { color: #3e463e; }
128
+ .art-pop b { max-width: 72%; font-size: 12cqw; font-weight: 800; letter-spacing: -1.5px; }
129
+ .pop-mark { position: absolute; width: 58%; aspect-ratio: 1; right: -18%; bottom: -29%; border: 29px solid #245dad; border-radius: 100%; }
130
+ .pop-star { position: absolute; font-size: 88px; right: 10%; top: -10%; color: #d5482b; }
131
+ .art-aula { background: #122927; color: #e6eadc; font-family: 'Cascadia Mono', Consolas, monospace; }
132
+ .art-aula small { color: #a5c5ae; font-size: 2.5cqw; letter-spacing: 0; }
133
+ .art-aula b { margin-top: 18px; font-family: 'Segoe UI', Arial, sans-serif; font-size: 10cqw; }
134
+ .art-aula em { font-style: normal; color: #bfdf6c; }
135
+ .code-lines { position: absolute; right: 14px; bottom: 12px; font-size: 3.8cqw; color: #91b69d; line-height: 1.7; opacity: .75; }
136
+ .code-cursor { position: absolute; width: 7px; height: 14px; background: #bfdf6c; bottom: 26px; left: 24px; }
137
+ .art-minimal { background: #eceef2; color: #202535; }
138
+ .art-minimal small { color: #748090; }
139
+ .art-minimal b { font-size: 11cqw; font-weight: 350; line-height: 1.16; max-width: 75%; margin-top: 17px; }
140
+ .minimal-line { position: absolute; width: 88px; height: 88px; border: 1px solid #9aacce; border-radius: 50%; right: -22px; bottom: -12px; }
141
+ .minimal-line::before { content: ''; position: absolute; inset: 12px; border: 1px solid #9aacce; border-radius: 50%; }
142
+ .experience-meta { display: flex; justify-content: space-between; align-items: center; gap: 8px; padding: 12px 1px 0; }
143
+ .experience-meta strong { font-size: 12px; font-weight: 600; }
144
+ .experience-meta .experience-sub { display: block; font-size: 10px; color: var(--text-3); margin-top: 4px; font-weight: 400; }
145
+ .experience-meta .arrow { display: grid; place-items: center; width: 23px; height: 23px; border: 1px solid var(--ui-line); border-radius: 50%; color: var(--text-3); transition: background .2s; }
146
+ .experience-card:hover .arrow { background: var(--selected); color: var(--brand); border-color: transparent; }
147
+ .catalog-heading { padding: 5px 0 26px; }
148
+ .catalog-heading h1 { font-size: 38px; letter-spacing: -1.3px; font-weight: 550; margin: 0 0 12px; }
149
+ .catalog-heading p { color: var(--text-2); line-height: 1.8; max-width: 560px; font-size: 12px; }
150
+ .recent-section { padding-top: 25px; border-top: 1px solid var(--ui-line); }
151
+ .head { display: flex; align-items: flex-end; gap: 12px; margin-bottom: 22px; flex-wrap: wrap; }
152
+ .head h1 { margin: 0; font-size: 25px; font-weight: 600; letter-spacing: -.65px; display: flex; align-items: center; gap: 10px; }
153
+ .head .sub { color: var(--text-3); margin-top: 6px; font-size: 11px; }
154
+ .sort { height: 32px; border-radius: 6px; border: 1px solid var(--ui-line); background: transparent; color: var(--text-2); padding: 0 9px; font: inherit; font-size: 10px; }
155
+ .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 24px 20px; }
156
+ .card { position: relative; border-radius: 9px; cursor: pointer; min-width: 0; }
157
+ .thumb { position: relative; aspect-ratio: 16 / 9; border-radius: 8px; overflow: hidden; background: var(--chrome-2); border: 1px solid var(--ui-line); transition: box-shadow .15s, transform .15s; }
158
+ .thumb img { width: 100%; height: 100%; object-fit: contain; display: block; position: relative; }
159
+ .thumb .loading { position: absolute; inset: 0; display: grid; place-items: center; color: var(--text-3); font-size: 12px; }
160
+ .card:hover .thumb { box-shadow: var(--shadow-2); transform: translateY(-2px); }
161
+ .card.dragging { opacity: .4; }
162
+ .over { position: absolute; inset: 0; display: flex; align-items: flex-end; justify-content: flex-end; gap: 6px; padding: 10px; opacity: 0; transition: opacity .15s; background: linear-gradient(transparent 45%, #10101870); }
163
+ .card:hover .over, .card:focus-within .over { opacity: 1; }
164
+ .pill { min-height: 30px; padding: 0 10px; border-radius: 6px; background: #fffffff2; color: #1a1a1a; font-size: 10px; font-weight: 600; display: inline-flex; align-items: center; gap: 5px; box-shadow: var(--shadow-1); border: 0; cursor: pointer; font-family: inherit; }
165
+ .pill .ic { width: 13px; height: 13px; }
166
+ .meta { display: flex; align-items: flex-start; gap: 6px; padding: 12px 2px 0; }
167
+ .meta .t { flex: 1; min-width: 0; }
168
+ .meta .name { font-weight: 600; font-size: 12px; line-height: 1.3; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
169
+ .meta .info { color: var(--text-3); font-size: 10px; margin-top: 6px; display: flex; gap: 5px; align-items: center; flex-wrap: wrap; }
170
+ .chip { display: inline-flex; align-items: center; gap: 2px; font-size: 10px; color: var(--text-3); }
171
+ .chip .dot { width: 5px; height: 5px; margin-left: 0; }
172
+ .more { width: 28px; height: 28px; border-radius: 6px; display: grid; place-items: center; color: var(--text-3); background: none; border: 0; }
173
+ .more:hover { background: var(--hover); color: var(--text); }
174
+ .new-card .thumb { border: 1px dashed var(--ui-line-strong); background: transparent; display: grid; place-items: center; color: var(--text-2); }
175
+ .new-card:hover .thumb { border-color: var(--brand); color: var(--brand); background: var(--selected); }
176
+ .new-card .plus { display: flex; flex-direction: column; align-items: center; gap: 8px; font-weight: 500; text-align: center; padding: 0 10px; font-size: 12px; }
177
+ .empty { text-align: center; padding: 45px 22px; color: var(--text-2); }
178
+ .empty .big { width: 44px; height: 44px; border-radius: 12px; background: var(--chrome-2); display: grid; place-items: center; margin: 0 auto 14px; color: var(--text-3); }
179
+ .empty .big .ic { width: 23px; height: 23px; }
180
+ .empty h2 { margin: 0 0 8px; font-size: 17px; color: var(--text); font-weight: 550; }
181
+ .empty p { margin: 0 auto 18px; max-width: 440px; line-height: 1.7; font-size: 12px; }
182
+ .empty .actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
183
+ .empty.compact { border: 1px dashed var(--ui-line-strong); border-radius: 10px; padding: 24px; text-align: left; display: flex; align-items: center; gap: 17px; }
184
+ .empty.compact .big { margin: 0; flex: none; }
185
+ .empty.compact .empty-copy { flex: 1; }
186
+ .empty.compact h2 { font-size: 14px; }
187
+ .empty.compact p { margin: 0; font-size: 11px; max-width: none; }
188
+ .trash-row { display: flex; align-items: center; gap: 14px; padding: 16px; border-radius: 8px; background: var(--chrome); border: 1px solid var(--ui-line); margin-bottom: 10px; flex-wrap: wrap; }
189
+ .trash-row .t { flex: 1; min-width: 180px; }
190
+ .trash-row .sub { color: var(--text-3); font-size: 11px; margin-top: 4px; }
191
+ .lmenu { position: fixed; z-index: 50; min-width: 250px; max-width: calc(100vw - 20px); background: var(--chrome); border: 1px solid var(--ui-line); border-radius: 12px; box-shadow: var(--shadow-3); padding: 7px; display: none; }
192
+ .lmenu.open { display: block; }
193
+ .mi { display: flex; align-items: center; gap: 12px; width: 100%; min-height: 36px; padding: 5px 10px; border-radius: 6px; text-align: left; background: none; border: 0; cursor: pointer; color: var(--text); font: inherit; font-size: 12px; }
194
+ .mi:hover { background: var(--hover); }
195
+ .mi small { margin-left: auto; color: var(--text-3); }
196
+ .mi.danger { color: var(--err); }
197
+ .mi .d { display: block; font-size: 10px; color: var(--text-3); margin-top: 4px; font-weight: 400; line-height: 1.4; }
198
+ .mi .ic { color: var(--text-3); }
199
+ .lmenu.tall .mi { padding: 12px; align-items: center; }
200
+ .menu-label { display: block; padding: 10px 12px 5px; font-size: 8px; font-weight: 650; color: var(--text-3); letter-spacing: 1.5px; }
201
+ .sep { height: 1px; background: var(--ui-line); margin: 5px 4px; }
202
+ .ldlg-back { position: fixed; inset: 0; background: #17142166; backdrop-filter: blur(5px); display: none; place-items: center; z-index: 60; padding: 20px; }
203
+ .ldlg-back.open { display: grid; }
204
+ .ldlg { width: min(460px, calc(100vw - 32px)); max-height: calc(100dvh - 40px); overflow-y: auto; background: var(--chrome); border: 1px solid var(--ui-line); border-radius: 16px; box-shadow: var(--shadow-3); padding: 25px; }
205
+ .ldlg h3 { margin: 0 0 16px; font-size: 22px; font-weight: 580; letter-spacing: -.5px; }
206
+ .ldlg label { display: block; font-size: 11px; color: var(--text-2); margin: 15px 0 7px; }
207
+ .ldlg input[type=text], .ldlg textarea { width: 100%; box-sizing: border-box; padding: 11px 12px; border-radius: 8px; border: 1px solid var(--ui-line-strong); background: var(--chrome); color: var(--text); font: inherit; font-size: 13px; }
208
+ .ldlg textarea { min-height: 135px; resize: vertical; line-height: 1.7; }
209
+ .ldlg .row { display: flex; justify-content: flex-end; gap: 8px; margin-top: 22px; }
210
+ .swatches { display: flex; gap: 8px; flex-wrap: wrap; }
211
+ .sw { width: 28px; height: 28px; border-radius: 8px; border: 3px solid transparent; cursor: pointer; }
212
+ .sw.on { border-color: var(--text); }
213
+ .ldlg .status { font-size: 12px; color: var(--text-2); margin-top: 10px; min-height: 1.2em; line-height: 1.6; }
214
+ .experience-dialog .experience-art { aspect-ratio: 16/8; margin: -7px -7px 21px; padding: 27px; }
215
+ .experience-dialog .experience-art b { font-size: 35px; }
216
+ .experience-dialog .experience-art small { font-size: 8px; }
217
+ .experience-description { color: var(--text-2); font-size: 12px; line-height: 1.8; margin: -3px 0 16px; }
218
+ .experience-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 15px; }
219
+ .experience-tags span { border: 1px solid var(--ui-line); background: var(--chrome-2); padding: 5px 8px; border-radius: 4px; font-size: 9px; color: var(--text-2); }
220
+ .dialog-note { font-size: 10px; line-height: 1.6; color: var(--text-3); margin: 12px 0 0; }
221
+ .ltoast { position: fixed; bottom: 22px; left: 50%; transform: translateX(-50%); background: var(--toast-bg); color: var(--toast-text); padding: 12px 17px; border-radius: 9px; box-shadow: var(--shadow-3); font-size: 12px; opacity: 0; transition: opacity .2s; pointer-events: none; z-index: 70; max-width: calc(100vw - 32px); }
222
+ .ltoast.show { opacity: 1; }
223
+ .initial-loading { display: flex; align-items: center; gap: 10px; color: var(--text-3); padding-top: 60px; justify-content: center; font-size: 12px; }
224
+ .loading-orbit { width: 15px; height: 15px; border: 2px solid var(--ui-line); border-top-color: var(--brand); border-radius: 100%; animation: spin .8s linear infinite; }
225
+ @keyframes spin { to { transform: rotate(360deg); } }
226
+ @media (min-width: 1680px) { .lib-main { padding: 40px 65px 70px; } .hero { padding-bottom: 48px; } .hero-stage { min-height: 320px; } .mini-dark { width: 335px; height: 207px; } .mini-dark b { font-size: 45px; } .mini-beige { left: 8%; } .mini-pop { right: 10%; } }
227
+ @media (max-width: 1200px) { body.lib { grid-template-columns: 190px minmax(0, 1fr); } .lib-top { padding: 0 24px; gap: 9px; } .lib-brand { padding: 0 20px; } .lib-main { padding: 30px 28px 60px; } .breadcrumb { display: none; } .hero { gap: 20px; } .hero-stage { transform: scale(.87); transform-origin: center right; margin-left: -40px; } .hero h1 { font-size: 44px; } .section-head h2 { font-size: 18px; } .experience-art { padding: 18px; } .experiences-grid { gap: 17px; } }
228
+ @media (max-width: 940px) { .hero { grid-template-columns: 1fr; padding-bottom: 30px; } .hero h1 { font-size: 45px; max-width: 480px; } .hero-stage { display: none; } .hero p { max-width: 420px; } .experiences-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } .experience-art { padding: 20px; } .empty.compact { flex-wrap: wrap; } }
229
+ @media (max-width: 700px) { body.lib { grid-template-columns: 1fr; grid-template-rows: 70px minmax(0, 1fr); } .lib-brand { position: absolute; left: 8px; height: 70px; padding: 0 10px; border: 0; font-size: 20px; } .lib-brand small { font-size: 6px; } .lib-logo { width: 27px; } .lib-top { padding: 0 15px 0 168px; gap: 7px; } .lib-top .spacer { display: none; } .lib-search { width: 100%; min-width: 0; padding: 0 8px; border: 0; } .lib-search kbd, #btn-new .lbl, .lib-user .uname, #btn-theme { display: none; } #btn-new { padding: 0; min-width: 35px; min-height: 35px; } .lib-body { display: grid; grid-template-rows: auto minmax(0, 1fr); min-height: 0; } .lib-side { padding: 8px 13px; overflow-x: auto; border-right: 0; border-bottom: 1px solid var(--ui-line); flex-direction: row; } #side { flex-direction: row; align-items: center; min-height: 0; flex: none; gap: 4px; } .nav { min-height: 33px; width: auto; white-space: nowrap; font-size: 11px; gap: 7px; padding: 0 11px; } .nav .ic { width: 15px; height: 15px; } .nav .count { display: none; } .side-title { margin: 0 3px; font-size: 0; } .side-title button { width: 25px; height: 30px; } .lib-side .grow, .studio-note, .where, .ai-settings { display: none; } .side-foot { padding: 0; border: 0; } .lib-main { padding: 27px 22px 45px; } .hero { padding-top: 0; } .hero h1 { font-size: 41px; letter-spacing: -1.8px; } .hero p { font-size: 12px; margin: 16px 0 21px; } .eyebrow { font-size: 8px; margin-bottom: 17px; } .hero-actions { gap: 15px; } .section-head { gap: 10px; align-items: flex-start; } .section-head h2 { font-size: 17px; max-width: 230px; } .section-head .text-button { font-size: 10px; } .experiences-section { padding-top: 24px; } .experience-filters { gap: 2px; margin-bottom: 17px; } .experience-filter { padding: 7px 9px; font-size: 9px; } .experiences-grid { gap: 17px 12px; } .experience-art { padding: 13px; } .experience-art b { margin-top: 13px; } .experience-art small { letter-spacing: .8px; } .experience-meta { padding-top: 9px; } .experience-meta strong { font-size: 10px; } .experience-meta .experience-sub { font-size: 8px; line-height: 1.4; } .experience-meta .arrow { display: none; } .pop-star { font-size: 53px; } .pop-mark { border-width: 18px; } .code-cursor { width: 4px; height: 9px; left: 14px; bottom: 16px; } .code-lines { display: none; } .executive-bars { right: 11px; bottom: 13px; } .experience-art b { font-size: 10cqw; } .catalog-heading h1 { font-size: 30px; } .catalog-heading p { font-size: 12px; } .head h1 { font-size: 23px; } .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 22px 12px; } .empty.compact { padding: 18px; gap: 12px; } .empty.compact .actions { justify-content: flex-start; } .empty.compact .empty-copy { min-width: 170px; } .over { padding: 6px; gap: 4px; opacity: 1; } .over .pill { font-size: 8px; padding: 0 6px; min-height: 25px; } .pill .ic { width: 10px; height: 10px; } .meta .info { font-size: 9px; } .meta .name { font-size: 11px; } .chip { font-size: 9px; } .ldlg { padding: 22px; } .ldlg .row .lib-btn { font-size: 11px; } }
230
+ @media (max-width: 390px) { .lib-search input { font-size: 10px; } .lib-main { padding-left: 17px; padding-right: 17px; } .hero h1 { font-size: 36px; } .lib-brand { font-size: 18px; gap: 6px; } .lib-top { padding-left: 149px; } .hero-actions { gap: 12px; } .hero-actions .lib-btn { font-size: 11px; } .section-head p { line-height: 1.6; } }
231
+ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation: none !important; transition: none !important; scroll-behavior: auto !important; } }
232
+ .lmenu.tall { max-width: 340px; } /* menu Nova: descrições quebram em vez de alargar o menu */
233
+
234
+ /* Nova: diálogo largo (IA em uma tela; vitrine "Modelo pronto") */
235
+ .ldlg.wide { width: min(760px, calc(100vw - 32px)); }
236
+ .ldlg select { width: 100%; box-sizing: border-box; padding: 10px 12px; border-radius: 8px; border: 1px solid var(--ui-line-strong); background: var(--chrome); color: var(--text); font: inherit; font-size: 13px; }
237
+ .ldlg option { background: var(--chrome); color: var(--text); }
238
+ .dlg-two { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); gap: 16px; }
239
+ .dlg-min { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-2); }
240
+ .dlg-min input { width: 84px; padding: 10px 12px; border-radius: 8px; border: 1px solid var(--ui-line-strong); background: var(--chrome); color: var(--text); font: inherit; font-size: 13px; }
241
+ .dlg-min .hint { color: var(--text-3); font-size: 12px; }
242
+ .dlg-att { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
243
+ .dlg-att input[type=text] { flex: 1; min-width: 180px; width: auto; }
244
+ .dlg-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
245
+ .dlg-chip { display: inline-flex; align-items: center; gap: 6px; max-width: 100%; padding: 4px 6px 4px 10px; border-radius: 999px; background: var(--selected); color: var(--text); font-size: 12px; }
246
+ .dlg-chip > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
247
+ .dlg-chip button { border: 0; background: none; color: var(--text-3); cursor: pointer; padding: 2px; display: inline-flex; }
248
+ .dlg-chip.reading { background: var(--hover); color: var(--text-3); padding-right: 10px; }
249
+ .dlg-chip .ic svg, .dlg-att .ic svg { width: 14px; height: 14px; }
250
+ .vit-filters { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 14px; }
251
+ .vit-filter { border: 1px solid var(--ui-line); background: none; border-radius: 999px; color: var(--text-2); font: inherit; font-size: 12px; padding: 6px 12px; cursor: pointer; }
252
+ .vit-filter:hover { background: var(--hover); }
253
+ .vit-filter.active { background: var(--text); color: var(--canvas-bg); border-color: var(--text); }
254
+ .vit-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; max-height: min(60vh, 560px); overflow-y: auto; padding: 2px; }
255
+ .vit-card { display: flex; flex-direction: column; gap: 6px; text-align: left; padding: 10px; border: 1px solid var(--ui-line); border-radius: 10px; background: var(--chrome); color: var(--text); font: inherit; cursor: pointer; }
256
+ .vit-card:hover, .vit-card:focus-visible { border-color: var(--brand); box-shadow: var(--shadow-2); outline: none; }
257
+ .vit-card[hidden] { display: none; }
258
+ .vit-card b { font-size: 13px; font-weight: 600; }
259
+ .vit-card .d { font-size: 11px; color: var(--text-3); line-height: 1.45; }
260
+ .vit-sw { display: flex; height: 54px; border-radius: 6px; overflow: hidden; margin-bottom: 4px; }
261
+ .vit-sw i { flex: 1; }
262
+ .vit-sw i:first-child { flex: 2.4; }
263
+ @media (max-width: 640px) { .dlg-two { grid-template-columns: 1fr; } }
264
+
265
+ /* vitrine Modelo pronto: capa desenhada de verdade (16:9) no lugar das barras de cor */
266
+ .vit-intro { margin: -4px 0 14px; color: var(--text-2); font-size: 13px; line-height: 1.5; }
267
+ .vit-thumb { display: block; width: 100%; aspect-ratio: 16 / 9; border-radius: 8px; overflow: hidden; margin-bottom: 10px; box-shadow: inset 0 0 0 1px var(--ui-line); }
268
+ .vit-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
269
+ .ldlg.drop-over { outline: 2px dashed var(--brand); outline-offset: -6px; }
270
+ .ldlg label .muted { color: var(--text-3); font-weight: 400; }
271
+ .dlg-ask { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border: 1px solid var(--brand); border-radius: 8px; color: var(--text); }
272
+ .dlg-ask b { font-weight: 600; font-size: 13px; }
273
+ .dlg-ask-opts { display: flex; flex-wrap: wrap; gap: 6px; }
274
+ .dlg-ask-free { display: flex; gap: 6px; }
275
+ .dlg-ask-free input { flex: 1; }