editorbrain 0.26.0 → 0.27.1

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.
@@ -1700,6 +1700,3250 @@ function validateIntegrationUpdate(providerId, input) {
1700
1700
  return { enabled: input.enabled, accessMethod: input.accessMethod, preferences, expectedRevision: input.expectedRevision };
1701
1701
  }
1702
1702
 
1703
+ // packages/core/src/authored-motion-data.ts
1704
+ var AUTHORED_MOTION_DATA = [
1705
+ {
1706
+ "id": "word-structure",
1707
+ "version": "1.0.0",
1708
+ "title": "Palabras que cargan peso",
1709
+ "mechanism": "typography-as-structure",
1710
+ "intent": "Dependencia, fragilidad y estabilidad: un concepto apoyado en una o varias bases.",
1711
+ "niche": "negocios",
1712
+ "style": "tipograf\xEDa",
1713
+ "duration": 12,
1714
+ "runtime": "canvas",
1715
+ "supportedRatios": [
1716
+ "16:9",
1717
+ "1:1",
1718
+ "9:16"
1719
+ ],
1720
+ "sourceSha256": "592d47817f890aff94b01786a37b0ce8b50ab8d173894b472481924ad84924fc",
1721
+ "cardSha256": "474d088994e32d971dc70377cbb24e2dda4d8399435998a5ecdb403c2e5d9744",
1722
+ "card": '# word-structure \xB7 Palabras que cargan peso\n\n**Mecanismo.** Las palabras son piezas estructurales: una palabra-viga (`copy.beam`) cae sobre una sola palabra-columna (`copy.single`) y tambalea; la columna se va, la viga se desploma al piso y sus letras se desacomodan. Tres palabras-columna (`copy.supports`) crecen debajo, la levantan a nivel y las letras vuelven a su sitio: dependencia de un solo apoyo \u2192 ca\xEDda \u2192 estabilidad por diversificaci\xF3n.\n\n**\xDAsala para.** Agencia/freelance: "dependes de un cliente" \u2192 tres fuentes de ingreso. Creador: un solo canal de tr\xE1fico vs. tres. Cl\xEDnica: un m\xE9dico estrella vs. equipo + agenda + protocolos. Ecommerce: un proveedor cr\xEDtico vs. redundancia. **No la uses para.** Listas de m\xE1s o menos de 3 apoyos (el c\xF3digo exige exactamente 3). Mensajes sin relaci\xF3n de "sostener" (rankings, antes/despu\xE9s num\xE9rico): la f\xEDsica se vuelve decoraci\xF3n. Vigas de varias palabras largas: se encogen hasta perder peso visual.\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| rise | 0.25 | El piso se dibuja (0\u20130.9 s); la columna `single` (accent) crece desde el piso. |\n| land | 1.45 | La viga cae y aterriza en 0.42 s; tambalea cada vez m\xE1s. `lineFragile` entra \u22482.2 s. |\n| leave | 4.35 | La columna se inclina y sale por la derecha; la viga cae girando. |\n| impact | 5.05 | Viga en el piso: sacudida 0.5 s, letras desordenadas. Entra `lineFall`. |\n| rise1 | 6.7 | Crece el apoyo izquierdo (`supports[0]`): la viga sube de un lado (inclinada). |\n| rise2 | 7.7 | Crece el apoyo derecho (`supports[2]`): la viga queda nivelada. |\n| rise3 | 8.7 | Crece el apoyo central (`supports[1]`) hasta tocar la viga. |\n| lock | 9.35 | Letras vuelven a su sitio con resorte; `lineStableA` y luego `lineStableB` subrayada (accent); zoom lento 2.5 %. |\n| end | 12 | Termina el zoom lento. Estado final completo desde \u224810.9 s. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand | kit (`santi`) y overrides de roles | cualquier kit de `lib/themes.js` |\n| duration, fps, timeScale | 12 s, 30 fps, escala global (cues incluidos) | si cambias `duration`, mueve `end` |\n| copy.beam | viga en `heavy`, ajustada al ancho del span (50/72/86 % W), tope 420u; se dibuja letra por letra | 1 palabra en MAY\xDASCULAS, \u2264 ~9 letras |\n| copy.single | columna \xFAnica vertical en accent, ajustada a la altura de columna | 1\u20132 palabras; m\xE1s larga = columna m\xE1s delgada |\n| copy.supports | 3 columnas verticales en ink, misma altura; el grosor sale del largo de cada palabra | exactamente 3: con <3 truena el setup, >3 se ignoran |\n| copy.lineFragile / lineFall | captions en `display` bajo el piso, alineadas al borde izquierdo de la viga | 1 l\xEDnea, sin wrap ni maxW |\n| copy.lineStableA / lineStableB | cierre en la misma l\xEDnea; B lleva subrayado a mano | A+B \u2264 ~38 car. en 9:16, ~70 en 16:9 |\n| beats.* | ver tabla | rise1<rise2<rise3 con \u22650.75 s entre s\xED (duraci\xF3n de cada subida) |\n| sound.cues | 10 cues anclados a beats | sfx: whoosh-short, thud, whoosh, crack, pop, snap, swell |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- Copy: viga, columna \xFAnica, los 3 apoyos y las 4 frases.\n- Beats con la voz: `land` en "todo descansa en\u2026", `leave`/`impact` en "si se va\u2026", `rise1..3` al nombrar cada apoyo, `lock` en la conclusi\xF3n.\n- Roles usados: `bg`, `ink` (piso, viga, apoyos, captions), `accent` (columna \xFAnica, subrayado). Fuentes: `heavy` (viga, columnas), `display` (captions).\n- `sound.cues`: gain/offset; vaciar el array deja la escena muda.\n\n## Qu\xE9 requiere tocar c\xF3digo\n- N\xFAmero de apoyos \u2260 3 (`supportX` y `riseOrder = [1,3,2]` fijos).\n- Captions en varias l\xEDneas, con wrap o en otra posici\xF3n.\n- Colores por pieza (columna \xFAnica = accent, apoyos = ink), direcci\xF3n de salida (siempre derecha), amplitud del tambaleo y la sacudida.\n- Proporciones por formato (`floorY`, `span`, `pillarH`).\n\n## Formatos\n- 16:9: piso al 80 % H, viga a 50 % W, columnas de 47 % H, captions 46u.\n- 1:1: piso al 77 % H, viga a 72 % W, columnas de 43 % H, captions 46u.\n- 9:16: piso al 73 % H, viga a 86 % W, columnas de 40 % H, captions 58u con m\xE1s aire debajo. Siempre centrada; no hay c\xE1mara salvo la sacudida y el zoom final.\n\n## Sonido\nwhoosh-short al subir la columna; thud suave al aterrizar la viga (+0.42); whoosh cuando la columna se va; thud + crack en el impacto; un pop por apoyo cuando toca la viga (+0.3\u20130.35); snap + swell en `lock`. Funciona muda: ca\xEDda y sacudida se leen sin audio.\n\n## Limitaciones\n- Exactamente 3 apoyos; no hay validaci\xF3n, con menos truena.\n- Captions sin wrap ni control de overflow: frases largas salen del cuadro (sobre todo 9:16).\n- El grosor de cada columna depende del largo de su palabra: largos muy distintos dan columnas desparejas.\n- El desorden de letras es pseudoaleatorio fijo (`K.hash`), no configurable.\n- Kit `santi` usa EB Baskerville / EB Avenir Next como fuentes locales de macOS; en otro renderer caen a fallback. Usa `brand.fonts` u otro kit si no existen.\n- Sin datos ni cifras: es una met\xE1fora pura.\n\n## Adaptar a otro tema\n- Agencia de marketing: `beam = "INGRESOS"`, `single = "META ADS"`, `supports = ["SEO","EMAIL","REFERIDOS"]`, `lineFragile = "Todo tu tr\xE1fico viene de un anuncio."`, `lineFall = "Si sube el CPM, se cae todo."`, `lineStableB = "Un sistema."`.\n- Cl\xEDnica dental: `beam = "CL\xCDNICA"`, `single = "DR. RUIZ"`, `supports = ["EQUIPO","AGENDA","PROTOCOLOS"]`, `lineFall = "Si \xE9l se va, se cae todo."`, `theme = "sage"`.\n- Ecommerce: `beam = "TIENDA"`, `single = "UN PROVEEDOR"`, `supports = ["LOCAL","IMPORT","PROPIO"]`, `lineFragile = "Todo tu stock sale de una f\xE1brica."`.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Inspiraci\xF3n: typography-as-structure \u2190 Gdgtify "BUILD THE FLOOR". Sin media de terceros (no hay `params.media`).\n',
1723
+ "bundle": {
1724
+ "objectKey": "authored-motion/1.0.0/word-structure/0c84d569d4dc505581b7a87b667927312b99b6b9f48171a5836dcebdd05f7ef2.tar.gz",
1725
+ "sha256": "0c84d569d4dc505581b7a87b667927312b99b6b9f48171a5836dcebdd05f7ef2",
1726
+ "bytes": 2934829,
1727
+ "fileCount": 58,
1728
+ "contentType": "application/gzip"
1729
+ },
1730
+ "previews": [
1731
+ {
1732
+ "ratio": "wide",
1733
+ "width": 1920,
1734
+ "height": 1080,
1735
+ "duration": 12,
1736
+ "video": {
1737
+ "objectKey": "authored-motion/1.0.0/word-structure/previews/7a51b723ca4a50423b917fe13ed2d51f80e0613affdcf3a7b6c70851ce5f0755.mp4",
1738
+ "sha256": "7a51b723ca4a50423b917fe13ed2d51f80e0613affdcf3a7b6c70851ce5f0755",
1739
+ "bytes": 1127216,
1740
+ "contentType": "video/mp4"
1741
+ },
1742
+ "poster": {
1743
+ "objectKey": "authored-motion/1.0.0/word-structure/previews/a9a848e10b5e25d5d0e827d9e5ede69d9ce6b97eef217db4d38c0a08ef1a1ef1.jpg",
1744
+ "sha256": "a9a848e10b5e25d5d0e827d9e5ede69d9ce6b97eef217db4d38c0a08ef1a1ef1",
1745
+ "bytes": 68718,
1746
+ "contentType": "image/jpeg"
1747
+ }
1748
+ },
1749
+ {
1750
+ "ratio": "square",
1751
+ "width": 1080,
1752
+ "height": 1080,
1753
+ "duration": 12,
1754
+ "video": {
1755
+ "objectKey": "authored-motion/1.0.0/word-structure/previews/84f334902c830ff3870a0fac7323b39e80967a0c966fcdce7002a7501f5ac0d3.mp4",
1756
+ "sha256": "84f334902c830ff3870a0fac7323b39e80967a0c966fcdce7002a7501f5ac0d3",
1757
+ "bytes": 924950,
1758
+ "contentType": "video/mp4"
1759
+ },
1760
+ "poster": {
1761
+ "objectKey": "authored-motion/1.0.0/word-structure/previews/908c62874d0c7aeb49b4d6b3eb8fdc87ddda6ba8ef592278c8d8e5b9e84e6b20.jpg",
1762
+ "sha256": "908c62874d0c7aeb49b4d6b3eb8fdc87ddda6ba8ef592278c8d8e5b9e84e6b20",
1763
+ "bytes": 57302,
1764
+ "contentType": "image/jpeg"
1765
+ }
1766
+ },
1767
+ {
1768
+ "ratio": "tall",
1769
+ "width": 1080,
1770
+ "height": 1920,
1771
+ "duration": 12,
1772
+ "video": {
1773
+ "objectKey": "authored-motion/1.0.0/word-structure/previews/66b6fbf974ec44c6cad112e4d04eb88671d6840b2232561f5527104b3fce89d4.mp4",
1774
+ "sha256": "66b6fbf974ec44c6cad112e4d04eb88671d6840b2232561f5527104b3fce89d4",
1775
+ "bytes": 1291814,
1776
+ "contentType": "video/mp4"
1777
+ },
1778
+ "poster": {
1779
+ "objectKey": "authored-motion/1.0.0/word-structure/previews/311ebf4c83324b5d5fd22838b3cea7df7fe59d469e299e4e81f4937d7907e468.jpg",
1780
+ "sha256": "311ebf4c83324b5d5fd22838b3cea7df7fe59d469e299e4e81f4937d7907e468",
1781
+ "bytes": 83236,
1782
+ "contentType": "image/jpeg"
1783
+ }
1784
+ }
1785
+ ],
1786
+ "qa": {
1787
+ "status": "source-and-preview-hashes-verified",
1788
+ "previewCount": 3,
1789
+ "artisticApproval": "not-certified",
1790
+ "audioListened": false,
1791
+ "transferCertified": false
1792
+ },
1793
+ "restrictions": [
1794
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
1795
+ "Scene-specific data/count/layout limits are documented in the selected card.",
1796
+ "Opaque output only; no alpha export.",
1797
+ "Voice/music requires timeScale=1 until explicitly retimed.",
1798
+ "Inspect frame boundaries, typography and final audio before delivery."
1799
+ ]
1800
+ },
1801
+ {
1802
+ "id": "one-object-order",
1803
+ "version": "1.0.0",
1804
+ "title": "Un objeto, todo el flujo",
1805
+ "mechanism": "persistent-ui-morph",
1806
+ "intent": "Demostrar un flujo de producto (pedido, pago, alta) como un solo objeto que cambia de funci\xF3n.",
1807
+ "niche": "restaurantes",
1808
+ "style": "UI",
1809
+ "duration": 12,
1810
+ "runtime": "canvas",
1811
+ "supportedRatios": [
1812
+ "16:9",
1813
+ "1:1",
1814
+ "9:16"
1815
+ ],
1816
+ "sourceSha256": "390ea4c85bb9b03e4ec0facb5cc32bbe1b57a8843072baae0b7b695444422538",
1817
+ "cardSha256": "b834ed380087d6f98653c469a70362a6a46799496d061dbdefa5eb7ac1c488ba",
1818
+ "card": '# one-object-order \xB7 Un objeto, todo el flujo\n\n**Mecanismo.** Un solo contenedor persiste y cambia de funci\xF3n: bot\xF3n \u2192 spinner \u2192 recibo \u2192 barra de seguimiento \u2192 pastilla "Entregado" + estrellas. El contenido sale antes de que el contenedor cambie de forma y entra cuando ya se asent\xF3; el color cambia por wipe circular. Consecuencia: el flujo entero se lee como una sola cosa que avanza, no como pantallas sueltas.\n\n**\xDAsala para.** Restaurante/delivery: del pedido a la entrega. Ecommerce: compra \u2192 pago \u2192 env\xEDo \u2192 recibido. Cl\xEDnica: reservar \u2192 confirmar \u2192 recordatorio \u2192 atendido. SaaS: alta \u2192 confirmaci\xF3n \u2192 primer resultado. **No la uses para.** Flujos con ramas o con m\xE1s/menos de 5 estados (fijos en c\xF3digo). Mostrar la UI real del cliente (esto es una UI gen\xE9rica ilustrativa). Comparar opciones o mostrar datos.\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| (inicio) | 0\u20130.7 | Pastilla accent entra con rebote; `status[0]` arriba; el cursor llega desde abajo-derecha. |\n| click | 1.35 | Click con ripple; la pastilla se aprieta, el texto sale y se encoge a c\xEDrculo con spinner (`status[1]`). |\n| card | 2.95 | El c\xEDrculo se abre en tarjeta-recibo (wipe accent\u2192surface): `shop`, items cada 0.35 s, l\xEDnea y `total` a +1.85 s (`status[2]`). |\n| track | 5.75 | El recibo sale; la tarjeta se aplana a barra con paradas `stops` (`status[3]`). |\n| drive | 6.55 | El repartidor (punto ink con estela) recorre la barra hasta `arrive`; la ETA baja en vivo; las paradas se encienden al pasar. |\n| arrive | 8.95 | Llega a la \xFAltima parada; la ETA desaparece. |\n| done | 9.15 | La barra se encoge a pastilla verde (wipe a `pos`) con check dibujado y `done` (`status[4]`); +0.9: `rate` y 5 estrellas vac\xEDas debajo. |\n| rate | 10.85 | El cursor vuelve y hace click en la 5.\xAA estrella; las 5 se encienden en cascada. |\n| end | 12 | No lo usa el c\xF3digo; la duraci\xF3n la fija `duration`. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand, duration, fps, timeScale | kit `trattoria`, 12 s | \u2014 |\n| copy.button | texto de la pastilla inicial (`heavy`); `{total}` \u2192 total calculado | pastilla de ancho fijo, sin fit: \u2264 ~14 car. ya reemplazado |\n| copy.shop | t\xEDtulo del recibo (`display`) | 1 l\xEDnea; maxW = ancho de tarjeta \u2212 m\xE1rgenes (solo avisa) |\n| copy.items | `[[cantidad, nombre, importe], \u2026]`; el total = suma de importes (la cantidad no multiplica), con el prefijo de moneda del primer importe y formato en-US | 3 c\xF3modos, 4 al l\xEDmite, 5+ desborda (alto fijo 560k); nombre \u2264 ~22 car. |\n| copy.totalLabel | etiqueta del total; el monto se calcula de `items` (no hay `copy.total`) | importes con punto decimal: "$12,50" se leer\xEDa como 1250 |\n| copy.stops | etiquetas de paradas | \u2265 2 (con 1 divide entre cero); 3\u20134 recomendado; sin wrap |\n| copy.etaFrom / etaUnit | ETA = `ceil(etaFrom\xB7(1\u2212progreso))` + unidad | entero; sin singular/plural ("1 d\xEDas") |\n| copy.done | texto de la pastilla final | ancho fijo: \u2264 ~11 car. |\n| copy.rate | pregunta sobre las estrellas | 1 l\xEDnea centrada |\n| copy.status | etiquetas mono en MAY\xDASCULAS sobre el objeto, una por estado | exactamente 5 |\n| beats.* | ver tabla | mantener el orden; \u2265 2.5 s entre `card` y `track` para leer el recibo |\n| sound.cues | 12 cues | los 3 `tick` (+0.75/+1.1/+1.45 tras `card`) est\xE1n alineados a 3 items |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- Copy completo: bot\xF3n, negocio, items (total y bot\xF3n se recalculan), paradas, ETA, estados, cierre.\n- Beats con la voz: `click` en "pides", `card` en "confirmas", `track`/`drive` en "sale", `done` en "llega".\n- Roles: `accent` (bot\xF3n, cantidades, progreso, estrellas), `surface` (tarjeta), `pos` (pastilla final), `line` (bordes, riel), `ink` (textos, repartidor, cursor), `muted` (status, "Total"), `accent2` (halo cuando el contenedor es surface), `bg`. Fuentes: `heavy`, `mono`, `display`, `text`.\n- Ajustar los ticks si cambias el n\xFAmero de items.\n\n## Qu\xE9 requiere tocar c\xF3digo\n- N\xFAmero, orden o forma de los estados (`R.pill/dot/card/track/done`).\n- M\xE1s de 4 items, tarjeta m\xE1s alta, logo o foto del producto.\n- Fit de texto en las pastillas; estrellas \u2260 5 o click en otra estrella.\n- Trayectoria del cursor (siempre entra desde abajo-derecha, borde blanco fijo).\n\n## Formatos\n- 16:9: centrado a 52 % H, escala k = 1.32u; la barra se limita a W \u2212 320u.\n- 1:1: centrado a 52 % H, k = 1.08u; la barra topa en 940 px.\n- 9:16: centrado a 50 % H, k = 1.38u; tarjeta y barra limitadas a W \u2212 120u. Misma composici\xF3n en los tres: solo cambia la escala.\n\n## Sonido\nclick en el click; whoosh-short al abrir la tarjeta, 3 ticks al entrar los items y coin al total; whoosh-short al pasar a barra; blip a medio recorrido y al llegar; success en "Entregado"; click + chime en la calificaci\xF3n. Funciona muda: cada estado se ve y la etiqueta `status` lo nombra.\n\n## Limitaciones\n- UI y precios ilustrativos; no reproduce la app del cliente.\n- Decimales: el total se muestra como n\xFAmero en-US sin forzar 2 decimales ("$189.5").\n- Pastillas de ancho fijo sin ajuste de texto; paradas largas se solapan.\n- Estados, 5 estrellas y comportamiento del cursor fijos.\n\n## Adaptar a otro tema\n- Ecommerce de ropa: `button = "Comprar \xB7 {total}"`, `shop = "Tienda Norte"`, `items = [["1\xD7","Chamarra M","$799"],["1\xD7","Env\xEDo","$100"]]` (total $899), `stops = ["Bodega","Paqueter\xEDa","Tu casa"]`, `etaFrom = 3`, `etaUnit = "d\xEDas"`, `status = ["Tu carrito","Procesando pago","Pago confirmado","En camino","Recibido"]`, `done = "Recibido"`.\n- Cl\xEDnica: `button = "Agendar cita"`, `shop = "Cl\xEDnica Sonrisa"`, `items = [["1\xD7","Limpieza","$600"],["1\xD7","Valoraci\xF3n","$0"]]` (total $600), `stops = ["Reservada","Recordatorio","Consulta"]`, `etaFrom = 48`, `etaUnit = "h"`, `done = "Atendido"`, `theme = "sage"`.\n- SaaS: `button = "Crear cuenta"`, items = plan/usuarios/integraci\xF3n, `stops = ["Cuenta","Datos","Primer reporte"]`, `etaUnit = "min"`, `theme = "indigo"`.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Inspiraci\xF3n: persistent object morph \u2190 twoclipping. Sin media de terceros (no hay `params.media`).\n',
1819
+ "bundle": {
1820
+ "objectKey": "authored-motion/1.0.0/one-object-order/0033a27b0eac4edec573472253d58e0edafc404eca9f40201ce0dc5280841d72.tar.gz",
1821
+ "sha256": "0033a27b0eac4edec573472253d58e0edafc404eca9f40201ce0dc5280841d72",
1822
+ "bytes": 2936282,
1823
+ "fileCount": 58,
1824
+ "contentType": "application/gzip"
1825
+ },
1826
+ "previews": [
1827
+ {
1828
+ "ratio": "wide",
1829
+ "width": 1920,
1830
+ "height": 1080,
1831
+ "duration": 12,
1832
+ "video": {
1833
+ "objectKey": "authored-motion/1.0.0/one-object-order/previews/47af0519ea09ed16d3f9a2ca9df52d2de2f88f7c09fad3cba21b9989cbdec79e.mp4",
1834
+ "sha256": "47af0519ea09ed16d3f9a2ca9df52d2de2f88f7c09fad3cba21b9989cbdec79e",
1835
+ "bytes": 1056038,
1836
+ "contentType": "video/mp4"
1837
+ },
1838
+ "poster": {
1839
+ "objectKey": "authored-motion/1.0.0/one-object-order/previews/828fdb326d276b2f0c98bc322b86ec70656bff68b8890b8732c788c8f6787c03.jpg",
1840
+ "sha256": "828fdb326d276b2f0c98bc322b86ec70656bff68b8890b8732c788c8f6787c03",
1841
+ "bytes": 40603,
1842
+ "contentType": "image/jpeg"
1843
+ }
1844
+ },
1845
+ {
1846
+ "ratio": "square",
1847
+ "width": 1080,
1848
+ "height": 1080,
1849
+ "duration": 12,
1850
+ "video": {
1851
+ "objectKey": "authored-motion/1.0.0/one-object-order/previews/c1c11ea0f82ff926a17bc34f8bb6eebdf608e9b1a6b9e30ae0850a9084df02a7.mp4",
1852
+ "sha256": "c1c11ea0f82ff926a17bc34f8bb6eebdf608e9b1a6b9e30ae0850a9084df02a7",
1853
+ "bytes": 821528,
1854
+ "contentType": "video/mp4"
1855
+ },
1856
+ "poster": {
1857
+ "objectKey": "authored-motion/1.0.0/one-object-order/previews/fe427ba6fd44648ef6f1a5496c0622cd8a425dfa15ad098b8727eb1e5fcc7dde.jpg",
1858
+ "sha256": "fe427ba6fd44648ef6f1a5496c0622cd8a425dfa15ad098b8727eb1e5fcc7dde",
1859
+ "bytes": 28863,
1860
+ "contentType": "image/jpeg"
1861
+ }
1862
+ },
1863
+ {
1864
+ "ratio": "tall",
1865
+ "width": 1080,
1866
+ "height": 1920,
1867
+ "duration": 12,
1868
+ "video": {
1869
+ "objectKey": "authored-motion/1.0.0/one-object-order/previews/45a484938b6826fa39b37794b559365330ee1de81a9d0c9ac9d0d40786288d94.mp4",
1870
+ "sha256": "45a484938b6826fa39b37794b559365330ee1de81a9d0c9ac9d0d40786288d94",
1871
+ "bytes": 1029528,
1872
+ "contentType": "video/mp4"
1873
+ },
1874
+ "poster": {
1875
+ "objectKey": "authored-motion/1.0.0/one-object-order/previews/d22c5a3a5bd5cdaf2c812c05e63eff94d199f73ec782fcddf1b108325fa7effa.jpg",
1876
+ "sha256": "d22c5a3a5bd5cdaf2c812c05e63eff94d199f73ec782fcddf1b108325fa7effa",
1877
+ "bytes": 40552,
1878
+ "contentType": "image/jpeg"
1879
+ }
1880
+ }
1881
+ ],
1882
+ "qa": {
1883
+ "status": "source-and-preview-hashes-verified",
1884
+ "previewCount": 3,
1885
+ "artisticApproval": "not-certified",
1886
+ "audioListened": false,
1887
+ "transferCertified": false
1888
+ },
1889
+ "restrictions": [
1890
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
1891
+ "Scene-specific data/count/layout limits are documented in the selected card.",
1892
+ "Opaque output only; no alpha export.",
1893
+ "Voice/music requires timeScale=1 until explicitly retimed.",
1894
+ "Inspect frame boundaries, typography and final audio before delivery."
1895
+ ]
1896
+ },
1897
+ {
1898
+ "id": "price-anatomy",
1899
+ "version": "1.0.0",
1900
+ "title": "Anatom\xEDa de un precio",
1901
+ "mechanism": "accumulating-vessel",
1902
+ "intent": "Desglosar un total en partes proporcionales y enfocar la parte que importa.",
1903
+ "niche": "cafeter\xEDas",
1904
+ "style": "ilustraci\xF3n",
1905
+ "duration": 12.5,
1906
+ "runtime": "canvas",
1907
+ "supportedRatios": [
1908
+ "16:9",
1909
+ "1:1",
1910
+ "9:16"
1911
+ ],
1912
+ "sourceSha256": "2226f533cd5eb05445ff876334f53b361891957b3717700b441faec11ed47187",
1913
+ "cardSha256": "fa057dd4d9485c9a1289e651ca19881822d596c6e729d8205fdf220ea62635c5",
1914
+ "card": '# price-anatomy \xB7 Anatom\xEDa de un precio\n\n**Mecanismo.** Un precio se vierte en un vaso capa por capa; la altura de cada capa es su proporci\xF3n del total (suma calculada de `layers`). Cada capa cuelga una etiqueta cuyo monto cuenta hacia arriba mientras se llena. Al final las dem\xE1s capas se aten\xFAan, la c\xE1mara empuja a la capa resaltada y un c\xEDrculo a mano la marca con su % calculado.\n\n**\xDAsala para.** Caf\xE9/restaurante: qu\xE9 paga un precio y cu\xE1nto es ganancia. Freelance: a d\xF3nde se va la tarifa (impuestos, software, horas no cobrables). Ecommerce/marketplace: costo de un pedido (producto, env\xEDo, comisi\xF3n, ads, margen). Hipoteca/renta: composici\xF3n de un pago. **No la uses para.** Cambios en el tiempo (no hay eje temporal). Partes que no suman un total o que restan (descuentos). M\xE1s de ~8 partes: las etiquetas se apilan fuera del vaso.\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| glass | 0.1 | El vaso se dibuja en trazo (0.9 s) con 4 marcas de regla; la pregunta (`copy.question`) entra 0.4\u20131.0 s. |\n| step | 1.12 | (duraci\xF3n) separaci\xF3n entre capas: la capa i empieza en `pour + i\xB7step`. |\n| pour | 1.25 | Primera capa: chorro desde arriba, superficie ondulada, etiqueta con monto contando; la c\xE1mara abre (pour+0.2 \u2192 pour+2.4). Con 6 capas el vertido termina \u22487.8 s. |\n| reveal | 8.55 | Sale la pregunta; las capas no resaltadas se aten\xFAan; la c\xE1mara empuja a la etiqueta resaltada (1.2 s); su monto crece y vira a `pos`. +0.9: elipse a mano; +1.5: `note` y `share` en letra `hand`. |\n| end | 12.5 | No lo usa el c\xF3digo. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand, duration, fps, timeScale | kit `cafe`, 12.5 s | si agregas capas, alarga `duration` |\n| currency | prefijo de todos los montos | montos redondeados a entero, sin separador de miles |\n| layers[] | `{name, amount, highlight?}` de abajo hacia arriba; altura = amount/total | 4\u20138 capas; color = `series[i % 6]` |\n| layers[].highlight | capa enfocada en `reveal`, pintada en `pos` | una sola; sin ninguna, el foco cae en la \xFAltima pero no cambia a `pos` y se aten\xFAa en el vaso |\n| copy.question | pregunta inicial; `{total}` \u2192 moneda + total calculado | wrap autom\xE1tico; \u2264 3 l\xEDneas; solo reemplaza el primer `{total}` |\n| copy.note | nota a mano sobre la capa resaltada | \u2264 ~20 car. |\n| copy.share | `{pct}` \u2192 % calculado de la capa resaltada (redondeado) | \u2264 ~20 car. |\n| beats.glass/pour/step/reveal | ver tabla | `reveal` \u2265 pour + n\xB7step + 0.3 |\n| sound.cues | solo los de `reveal`: whoosh, scribble (+0.95), coin (+1.7) | los `pour` los genera `cues(S)` (uno por capa) y se suman a estos |\n\nNombres: en 16:9 y 1:1 van en una l\xEDnea junto al monto (maxW solo avisa); en 9:16 van bajo el monto con wrap a 2 l\xEDneas (\u2264 300u). Las etiquetas se separan solas (gap 1.5\xD7 el tama\xF1o, 2.15\xD7 en 9:16).\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- `layers` (nombres, montos, cu\xE1l se resalta), `currency`, `question/note/share`: total y % se recalculan.\n- Beats con la voz: `pour` en "esto es lo que hay dentro", `reveal` en "y esto es lo que te queda". Los `pour` siguen solos al n\xFAmero de capas y a `step`.\n- Roles: `series[]` (capas), `pos` (capa y monto resaltados), `ink` (vaso, gu\xEDas, montos), `surface` (brillo del vaso), `muted` (nombres), `accent` (elipse y notas), `bg` (atenuado). Fuentes: `display` (pregunta, montos), `text` (nombres), `hand` (nota, %).\n\n## Qu\xE9 requiere tocar c\xF3digo\n- Forma del recipiente (vaso trapezoidal fijo) u orientaci\xF3n.\n- Decimales o separador de miles en montos.\n- Resaltar m\xE1s de una capa, partes negativas, % relativo a otro total.\n- Etiquetas a ambos lados en 16:9/1:1.\n- Volumen o SFX del vertido (`cues(S)`).\n\n## Formatos\n- 16:9: vaso a 33 % W y 60 % H de alto; etiquetas en columna a la derecha; pregunta arriba a la derecha (20 % H, ancho 36 % W).\n- 1:1: vaso a 30 % W y 54 % H; misma columna derecha; pregunta arriba a ancho completo (86 % W).\n- 9:16: vaso centrado de 36 % H; etiquetas alternan izquierda (pares) / derecha con monto arriba y nombre debajo; la c\xE1mara arranca cerca del vaso (1.14\xD7) y abre.\n\n## Sonido\nUn `pour` por capa generado por `cues(S)` 0.15 s antes de que empiece cada capa (ganancia 0.5 fija en c\xF3digo); whoosh en el empuje de c\xE1mara; scribble mientras se dibuja la elipse; coin al aparecer el %. Funciona muda.\n\n## Limitaciones\n- Datos del ejemplo ilustrativos (precio de caf\xE9 inventado); total y % s\xED son calculados.\n- Montos redondeados a enteros: precios con centavos pierden precisi\xF3n.\n- Capas < ~3 % del total son casi invisibles en el vaso aunque la etiqueta se lea.\n- Nombres largos en 16:9/1:1 no hacen wrap.\n\n## Adaptar a otro tema\n- Freelance dise\xF1o: `layers = [{name:"Impuestos",amount:16},{name:"Software",amount:3},{name:"Comisi\xF3n plataforma",amount:10},{name:"Horas no cobrables",amount:21},{name:"Tu ingreso real",amount:50,highlight:true}]`, `question = "\xBFQu\xE9 pasa con cada {total} que cobras?"`, `note = "lo que s\xED cobras"`, `theme = "ledger"`.\n- Ecommerce en marketplace: `layers = [{Producto 180},{Env\xEDo 70},{Comisi\xF3n 90},{Publicidad 60},{Devoluciones 20},{Margen 40, highlight}]`, `question = "\xBFQu\xE9 hay dentro de un pedido de {total}?"`.\n- Hipoteca: 4 capas (Capital, Intereses con highlight, Seguro, Comisiones), `note = "lo que se queda el banco"`, `share = "{pct}% de tu pago"`.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Inspiraci\xF3n: accumulation \u2190 Ror_Fly (vaso que se llena con cantidades visibles). Sin media de terceros (no hay `params.media`).\n',
1915
+ "bundle": {
1916
+ "objectKey": "authored-motion/1.0.0/price-anatomy/514358e6aa4932dd1891161538313b12267e7ee0af6ff8ddf4148dbe4e147c48.tar.gz",
1917
+ "sha256": "514358e6aa4932dd1891161538313b12267e7ee0af6ff8ddf4148dbe4e147c48",
1918
+ "bytes": 2936183,
1919
+ "fileCount": 58,
1920
+ "contentType": "application/gzip"
1921
+ },
1922
+ "previews": [
1923
+ {
1924
+ "ratio": "wide",
1925
+ "width": 1920,
1926
+ "height": 1080,
1927
+ "duration": 12.5,
1928
+ "video": {
1929
+ "objectKey": "authored-motion/1.0.0/price-anatomy/previews/de94c837514b16ad41bb9cbb0a6726f90c7b8591aa94267e398b497e9e5ed029.mp4",
1930
+ "sha256": "de94c837514b16ad41bb9cbb0a6726f90c7b8591aa94267e398b497e9e5ed029",
1931
+ "bytes": 901717,
1932
+ "contentType": "video/mp4"
1933
+ },
1934
+ "poster": {
1935
+ "objectKey": "authored-motion/1.0.0/price-anatomy/previews/8b9e8fc54ad0cf378d8f43f95bd9ac88b344f593eea79820f697a80a2879d053.jpg",
1936
+ "sha256": "8b9e8fc54ad0cf378d8f43f95bd9ac88b344f593eea79820f697a80a2879d053",
1937
+ "bytes": 73283,
1938
+ "contentType": "image/jpeg"
1939
+ }
1940
+ },
1941
+ {
1942
+ "ratio": "square",
1943
+ "width": 1080,
1944
+ "height": 1080,
1945
+ "duration": 12.5,
1946
+ "video": {
1947
+ "objectKey": "authored-motion/1.0.0/price-anatomy/previews/bfb952787e9ecab40dd153823a300800ba74e36e5879eaad4bece0ca92b9f1d5.mp4",
1948
+ "sha256": "bfb952787e9ecab40dd153823a300800ba74e36e5879eaad4bece0ca92b9f1d5",
1949
+ "bytes": 823547,
1950
+ "contentType": "video/mp4"
1951
+ },
1952
+ "poster": {
1953
+ "objectKey": "authored-motion/1.0.0/price-anatomy/previews/8157cfe637daa9085905b593117dab55b17c6254c06cb1c450aa7e64aa006f52.jpg",
1954
+ "sha256": "8157cfe637daa9085905b593117dab55b17c6254c06cb1c450aa7e64aa006f52",
1955
+ "bytes": 58699,
1956
+ "contentType": "image/jpeg"
1957
+ }
1958
+ },
1959
+ {
1960
+ "ratio": "tall",
1961
+ "width": 1080,
1962
+ "height": 1920,
1963
+ "duration": 12.5,
1964
+ "video": {
1965
+ "objectKey": "authored-motion/1.0.0/price-anatomy/previews/db365ca209a52d3690a5b182418976c316c5b3d1695f28ce2a5b40a47f75b542.mp4",
1966
+ "sha256": "db365ca209a52d3690a5b182418976c316c5b3d1695f28ce2a5b40a47f75b542",
1967
+ "bytes": 976273,
1968
+ "contentType": "video/mp4"
1969
+ },
1970
+ "poster": {
1971
+ "objectKey": "authored-motion/1.0.0/price-anatomy/previews/9720784c1c2f3217ef81773238f61f04c600ecc55a73c9bd71a7f749e2ab88d6.jpg",
1972
+ "sha256": "9720784c1c2f3217ef81773238f61f04c600ecc55a73c9bd71a7f749e2ab88d6",
1973
+ "bytes": 58358,
1974
+ "contentType": "image/jpeg"
1975
+ }
1976
+ }
1977
+ ],
1978
+ "qa": {
1979
+ "status": "source-and-preview-hashes-verified",
1980
+ "previewCount": 3,
1981
+ "artisticApproval": "not-certified",
1982
+ "audioListened": false,
1983
+ "transferCertified": false
1984
+ },
1985
+ "restrictions": [
1986
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
1987
+ "Scene-specific data/count/layout limits are documented in the selected card.",
1988
+ "Opaque output only; no alpha export.",
1989
+ "Voice/music requires timeScale=1 until explicitly retimed.",
1990
+ "Inspect frame boundaries, typography and final audio before delivery."
1991
+ ]
1992
+ },
1993
+ {
1994
+ "id": "route-untangle",
1995
+ "version": "1.0.0",
1996
+ "title": "La ruta se desenreda",
1997
+ "mechanism": "order-morph-on-map",
1998
+ "intent": "Mismos elementos, mejor orden: rutas, procesos, prioridades; cifras derivadas de la geometr\xEDa.",
1999
+ "niche": "log\xEDstica",
2000
+ "style": "mapa",
2001
+ "duration": 11,
2002
+ "runtime": "canvas",
2003
+ "supportedRatios": [
2004
+ "16:9",
2005
+ "1:1",
2006
+ "9:16"
2007
+ ],
2008
+ "sourceSha256": "d2fbd8eea3702ca74f2b0e20056985d1e9ea6ac1a04b244baf4dc242038bd60f",
2009
+ "cardSha256": "5518b50e9b9b15e4a20a1e71b093e98125cf7299e9700358e1196e90c67fdf18",
2010
+ "card": '# route-untangle \xB7 La ruta se desenreda\n\n**Mecanismo.** Las mismas paradas, dos \xF3rdenes. La ruta planeada (`orderBefore`) se traza y sus cruces se marcan a mano; luego cada v\xE9rtice se desliza a su lugar en el orden optimizado (calculado: vecino m\xE1s cercano + 2-opt) y la l\xEDnea se re-enhebra como una cuerda: los cruces desaparecen y tiempo/km bajan. Un veh\xEDculo recorre la ruta nueva y cada parada se palomea.\n\n**\xDAsala para.** Reparto/log\xEDstica: optimizar entregas del d\xEDa. T\xE9cnicos o ventas de campo: orden de visitas. Servicios a domicilio (limpieza, mantenimiento). Almac\xE9n: recorrido de picking (con unidades adaptadas, ver l\xEDmites). **No la uses para.** Mensajes de agregar/quitar paradas (aqu\xED solo cambia el orden). Mapas geogr\xE1ficos reales (el mapa es decorativo). Planes originales sin cruces: mostrar\xEDa "0 cruces".\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| pins | 0.6 | Mapa ya revelado radialmente desde el dep\xF3sito (0\u20130.9 s); aparecen dep\xF3sito y paradas numeradas en orden planeado (cada 0.09 s). |\n| draw | 1.9 | Entra el panel HUD; la ruta planeada (neg) se traza en 2.4 s con el veh\xEDculo en la punta; tiempo y km crecen con el trazo. |\n| cross | 4.55 | Cada cruce se circula a mano en neg (cada 0.12 s); aparece "{n} cruces"; el veh\xEDculo se oculta. |\n| morph | 5.9 | V\xE9rtices al orden \xF3ptimo (1.6 s); la l\xEDnea pasa de neg a accent; cruces se borran; HUD baja a los valores nuevos. +1.2: los n\xFAmeros de las paradas giran al nuevo orden. +1.6: badge `saved` (pos) y `savedKm`. |\n| drive | 7.7 | Trazo accent grueso recorre la ruta nueva (2.6 s) con el veh\xEDculo; cada parada alcanzada se vuelve pos con palomita. +0.6: frase `after`. |\n| end | 11 | Solo lo usa el cue `chime` (end \u2212 1.0). |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand, duration, fps, timeScale | kit `transit`, 11 s | \u2014 |\n| copy.hudLabel | encabezado del HUD en MAY\xDASCULAS mono; `{n}` = n\xFAmero de paradas | 1 l\xEDnea; en 16:9 el panel mide 24 % W |\n| copy.crossings | `{n}` = cruces calculados del plan | \u2014 |\n| copy.saved / savedKm | `{min}` minutos ahorrados, `{km}` km ahorrados (entero) | calculados |\n| copy.after | frase final | 1 l\xEDnea sin wrap (44\u201346u) |\n| route.depot / stops | `[x,y]` normalizados 0\u20131 dentro del rect\xE1ngulo del mapa | 6\u201312 paradas; con m\xE1s, los pins (r 25u) se tocan |\n| route.orderBefore | orden planeado (\xEDndices de `stops`) | permutaci\xF3n completa obligatoria; debe generar cruces (3\u20136 es cre\xEDble) |\n| route.kmBefore | km del plan; fija la escala km/unidad | km despu\xE9s = kmBefore \xD7 largo \xF3ptimo / largo plan |\n| route.speedKmh / minutesPerStop | minutos = km/vel\xB760 + paradas\xB7min por parada | \u2014 |\n| beats.* | ver tabla | `morph` \u2265 cross + 0.12\xB7cruces + 0.5 |\n| sound.cues | 11 cues | solo 3 `pop` para todos los pins |\n\nCalculado, no editable: orden \xF3ptimo, cruces, km/min antes y despu\xE9s, ahorro. Con los datos de ejemplo: 5 cruces, 2 h 21 \u2192 1 h 48 min, 61 \u2192 43.2 km (\u221233 min, \u221218 km). Formatos "X h MM min" y "x.x km" fijos.\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- `route` completo (posiciones, orden planeado, km, velocidad, minutos por parada): todas las cifras se recalculan.\n- Copy del HUD, cruces, ahorro y cierre; beats con la voz (`cross` en "se cruza X veces", `morph` en "mismo reparto, otro orden").\n- Roles: `bg`+`surface` (base del mapa), `line` (calles), `pos` (parque, paradas cumplidas, badge), `accent2` (r\xEDo), `neg` (ruta planeada, cruces), `accent` (ruta nueva, trazo, ventana del veh\xEDculo), `ink` (dep\xF3sito, pins, veh\xEDculo), `muted` (HUD). Fuentes: `mono`, `display`, `heavy`, `hand`, `text` (glifo \u2302).\n\n## Qu\xE9 requiere tocar c\xF3digo\n- Mapa real, o calles/parque/r\xEDo en otra posici\xF3n (decoraci\xF3n fija en coordenadas normalizadas, calles con semilla).\n- Unidades (km, h/min) y formato; varios veh\xEDculos o dep\xF3sitos; imponer un orden "\xF3ptimo" a mano.\n- Cambio de color por wipe (hoy interpola neg\u2192accent); \xEDcono del dep\xF3sito.\n\n## Formatos\n- 16:9: HUD a la izquierda (24 % W); mapa a la derecha (30\u201396 % W, 86 % H); frase final abajo a la izquierda.\n- 1:1: HUD como banda superior a todo el ancho; mapa abajo (28\u201394 % H); cruces y badge en la parte derecha de la banda; frase centrada bajo el mapa.\n- 9:16: igual que 1:1 con mapa en 27\u201391 % H y frase debajo.\n\n## Sonido\n3 pops escalonados con los pins; whoosh al trazar; dos scribbles al circular cruces; riser antes del morph; snap + coin cuando aparece el ahorro; whoosh-short al arrancar el recorrido; chime 1 s antes del final. Funciona muda: HUD y badge cargan el resultado.\n\n## Limitaciones\n- Mapa ilustrativo y fijo; una parada puede caer sobre el r\xEDo o el parque.\n- Distancias en espacio normalizado (iguales en los 3 formatos); km reales solo v\xEDa `kmBefore`.\n- `savedKm` redondea a entero: en recorridos cortos (< 2 km) muestra "\u22120 km".\n- Si `orderBefore` no incluye todas las paradas, el morph se rompe; sin cruces, la escena pierde su argumento.\n- La interpolaci\xF3n neg\u2192accent puede verse sucia en kits donde esos colores est\xE9n lejos.\n- Frase `after` sin wrap.\n\n## Adaptar a otro tema\n- T\xE9cnicos de mantenimiento: `hudLabel = "Visitas de hoy \xB7 {n} clientes"`, 7 `stops`, `orderBefore` seg\xFAn la agenda real, `kmBefore = 48`, `speedKmh = 28`, `minutesPerStop = 40`, `after = "Mismas visitas. Otro orden."`.\n- Distribuidora de bebidas: `hudLabel = "Ruta \xB7 {n} tiendas"`, 10 `stops`, `kmBefore = 85`, `minutesPerStop = 12`, `saved = "\u2212{min} min por ruta"`, `theme = "signal"`.\n- Picking en almac\xE9n: `hudLabel = "Pedido \xB7 {n} ubicaciones"`, `kmBefore = 1.4`, `speedKmh = 4`, `minutesPerStop = 1`, `savedKm = ""` (evita "\u22120 km").\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Mecanismo original. Sin media de terceros (no hay `params.media`).\n',
2011
+ "bundle": {
2012
+ "objectKey": "authored-motion/1.0.0/route-untangle/65c69a17cdd53df32ab0c072c85129ab5d77b30bdf3248e8e0b3ccf79dbe6779.tar.gz",
2013
+ "sha256": "65c69a17cdd53df32ab0c072c85129ab5d77b30bdf3248e8e0b3ccf79dbe6779",
2014
+ "bytes": 2936581,
2015
+ "fileCount": 58,
2016
+ "contentType": "application/gzip"
2017
+ },
2018
+ "previews": [
2019
+ {
2020
+ "ratio": "wide",
2021
+ "width": 1920,
2022
+ "height": 1080,
2023
+ "duration": 11,
2024
+ "video": {
2025
+ "objectKey": "authored-motion/1.0.0/route-untangle/previews/ed2d85903de303f632f929b8d1eebb828fc7c958848ff530a426b14e555632df.mp4",
2026
+ "sha256": "ed2d85903de303f632f929b8d1eebb828fc7c958848ff530a426b14e555632df",
2027
+ "bytes": 1177103,
2028
+ "contentType": "video/mp4"
2029
+ },
2030
+ "poster": {
2031
+ "objectKey": "authored-motion/1.0.0/route-untangle/previews/4bc9505aca66b81e462c24d55af61c36da92a8207c294848b722fd8d99de8e48.jpg",
2032
+ "sha256": "4bc9505aca66b81e462c24d55af61c36da92a8207c294848b722fd8d99de8e48",
2033
+ "bytes": 162977,
2034
+ "contentType": "image/jpeg"
2035
+ }
2036
+ },
2037
+ {
2038
+ "ratio": "square",
2039
+ "width": 1080,
2040
+ "height": 1080,
2041
+ "duration": 11,
2042
+ "video": {
2043
+ "objectKey": "authored-motion/1.0.0/route-untangle/previews/73cc7145049068f37e7f26e481e8af90bd7b045887b6ec2d7be55a7ff2b1a914.mp4",
2044
+ "sha256": "73cc7145049068f37e7f26e481e8af90bd7b045887b6ec2d7be55a7ff2b1a914",
2045
+ "bytes": 984488,
2046
+ "contentType": "video/mp4"
2047
+ },
2048
+ "poster": {
2049
+ "objectKey": "authored-motion/1.0.0/route-untangle/previews/0156ce3fefb8232603763872127e7dc83f6a3e5dc02249efc7a71d25106d4d5e.jpg",
2050
+ "sha256": "0156ce3fefb8232603763872127e7dc83f6a3e5dc02249efc7a71d25106d4d5e",
2051
+ "bytes": 119573,
2052
+ "contentType": "image/jpeg"
2053
+ }
2054
+ },
2055
+ {
2056
+ "ratio": "tall",
2057
+ "width": 1080,
2058
+ "height": 1920,
2059
+ "duration": 11,
2060
+ "video": {
2061
+ "objectKey": "authored-motion/1.0.0/route-untangle/previews/6c782824d0f3e9bbc1c58cd4a5989c245500d9cd5705fda537f8f35448302b2c.mp4",
2062
+ "sha256": "6c782824d0f3e9bbc1c58cd4a5989c245500d9cd5705fda537f8f35448302b2c",
2063
+ "bytes": 1169391,
2064
+ "contentType": "video/mp4"
2065
+ },
2066
+ "poster": {
2067
+ "objectKey": "authored-motion/1.0.0/route-untangle/previews/109b4287490f3a9290b62012e509cfa1fea08b15e33e6065fe5d3fb2144c2032.jpg",
2068
+ "sha256": "109b4287490f3a9290b62012e509cfa1fea08b15e33e6065fe5d3fb2144c2032",
2069
+ "bytes": 166082,
2070
+ "contentType": "image/jpeg"
2071
+ }
2072
+ }
2073
+ ],
2074
+ "qa": {
2075
+ "status": "source-and-preview-hashes-verified",
2076
+ "previewCount": 3,
2077
+ "artisticApproval": "not-certified",
2078
+ "audioListened": false,
2079
+ "transferCertified": false
2080
+ },
2081
+ "restrictions": [
2082
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
2083
+ "Scene-specific data/count/layout limits are documented in the selected card.",
2084
+ "Opaque output only; no alpha export.",
2085
+ "Voice/music requires timeScale=1 until explicitly retimed.",
2086
+ "Inspect frame boundaries, typography and final audio before delivery."
2087
+ ]
2088
+ },
2089
+ {
2090
+ "id": "thread-repurpose",
2091
+ "version": "1.0.0",
2092
+ "title": "Un hilo, cinco piezas",
2093
+ "mechanism": "continuous-thread-branching",
2094
+ "intent": "Una fuente que se ramifica en salidas; la c\xE1mara viaja y al final muestra el sistema.",
2095
+ "niche": "creadores",
2096
+ "style": "UI",
2097
+ "duration": 14,
2098
+ "runtime": "canvas",
2099
+ "supportedRatios": [
2100
+ "16:9",
2101
+ "1:1",
2102
+ "9:16"
2103
+ ],
2104
+ "sourceSha256": "b792640aa69fa6ee45e6de49e4a85e1bbb9dddd36a45bdb7c81a4e92a3d3ff54",
2105
+ "cardSha256": "9646a5b40fc2beb208556e3004b41c790f14118373ca4a8b6f5c7a10c4dff46d",
2106
+ "card": '# thread-repurpose \xB7 Un hilo, cinco piezas\n\n**Mecanismo.** Un hilo continuo (la grabaci\xF3n) atraviesa toda la escena como l\xEDnea de tiempo. Un pulso accent viaja por \xE9l y en cada estaci\xF3n el hilo se ramifica en una tarjeta de salida: fuente \u2192 transcripci\xF3n \u2192 clips \u2192 cita \u2192 newsletter. La c\xE1mara sigue al pulso y al final se aleja para mostrar el sistema completo con la frase de cierre.\n\n**\xDAsala para.** Podcast/creador: un episodio \u2192 5 piezas. Webinar o curso: una sesi\xF3n \u2192 materiales derivados. Agencia de contenido: explicar su proceso de repurposing. Marca personal: una entrevista \u2192 posts y newsletter. **No la uses para.** Salidas distintas de fuente/transcripci\xF3n/clips/cita/email (cada tarjeta tiene layout propio fijo). Procesos de \u2260 5 pasos. Flujos con varias ramas por nodo o sin orden temporal.\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| source | 0.2 | El hilo se enciende desde la izquierda (0\u20131.2 s); rama y tarjeta 01: `source`, `sourceMeta`, forma de onda animada. |\n| s1 | 2.4 | El pulso viaja 0.9 s y llega; tarjeta 02: la transcripci\xF3n se escribe l\xEDnea a l\xEDnea; l\xEDneas 2\u20133 se resaltan (accent2) a +2.1 s. |\n| s2 | 4.6 | Tarjeta 03: 3 miniaturas verticales con su duraci\xF3n. |\n| s3 | 6.8 | Tarjeta 04: comilla accent, `quote` con wrap y `quoteBy`. |\n| s4 | 9.0 | Tarjeta 05: `mailSubject`, 4 l\xEDneas de cuerpo y bot\xF3n `mailCta`. |\n| pull | 10.9 | La c\xE1mara se aleja en 1.4 s hasta encuadrar las 5 tarjetas; +1.1: `closing` y `closingSub` abajo, centrados. |\n| end | 14 | No lo usa el c\xF3digo. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand, duration, fps, timeScale | kit `night` (oscuro), 14 s | \u2014 |\n| copy.source / sourceMeta | t\xEDtulo de la tarjeta 1 (`display` 60u) y su etiqueta | t\xEDtulo \u2264 ~18 car., sin maxW |\n| copy.transcriptTag, clipsTag, quoteTag, mailTag | etiquetas "0N \xB7 TAG" en MAY\xDASCULAS mono | \u2264 ~35 car. (maxW solo avisa) |\n| copy.transcript | l\xEDneas `"timestamp\u2423\u2423texto"` (separador: dos espacios) | m\xE1x. 4 l\xEDneas; texto \u2264 ~26 car.; siempre resalta las l\xEDneas 2 y 3 |\n| copy.clipDurations | etiqueta de cada miniatura | layout calculado para exactamente 3 |\n| copy.quote / quoteBy | cita (`display` 50u, wrap \u224820 car./l\xEDnea) y autor | \u2264 4 l\xEDneas (~80 car.) |\n| copy.mailSubject | asunto (`heavy` 33u, 1 l\xEDnea) | \u2264 ~32 car. |\n| copy.mailCta | texto del bot\xF3n de la tarjeta 5 | bot\xF3n de ancho fijo 210u: \u2264 ~12 car. |\n| copy.closing / closingSub | cierre en pantalla | 1 l\xEDnea cada uno, sin wrap; \u2264 ~24 car. en 9:16 |\n| beats.* | ver tabla | \u2265 2 s entre estaciones (viaje 0.9 + tarjeta 0.8 + contenido ~1.5) |\n| sound.cues | whoosh-short (\u22120.7) + blip por estaci\xF3n; whoosh + chime en `pull` | \u2014 |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- Todos los textos, duraciones de clips y timestamps.\n- Beats con la voz: cada `s1..s4` cuando se nombra la pieza; `pull` en la conclusi\xF3n.\n- Roles: `bg`, `surface` (tarjetas), `line` (hilo base, bordes), `accent` (hilo encendido, pulso, ramas, comilla, bot\xF3n, onda reproducida), `accent2` (resaltado de transcripci\xF3n), `muted` (marcas, etiquetas, onda, l\xEDneas de email), `ink`, `series[0..2]` (miniaturas). Fuentes: `display`, `text`, `mono`, `heavy`.\n\n## Qu\xE9 requiere tocar c\xF3digo\n- N\xFAmero o tipo de estaciones (array `ST` fijo de 5 con una funci\xF3n de dibujo por tarjeta).\n- M\xE1s/menos de 3 clips; miniaturas con frames reales (hoy son gradiente + barras).\n- Qu\xE9 l\xEDneas de transcripci\xF3n se resaltan (siempre 2 y 3).\n- Sombra fija `rgba(0,0,0,0.45)` (pesada en kits claros); posici\xF3n de tarjetas.\n\n## Formatos\n- 16:9: hilo horizontal, tarjetas alternan arriba (pares) / abajo, separaci\xF3n 980u; zoom inicial 1.3\xD7.\n- 1:1: mismo layout horizontal, separaci\xF3n 900u, zoom inicial 1.12\xD7.\n- 9:16: hilo vertical a 170u del borde izquierdo, tarjetas apiladas a la derecha, separaci\xF3n 780u, zoom 1\xD7. En el `pull` el zoom baja a \u22480.35\u20130.38\xD7.\n\n## Sonido\nCada estaci\xF3n: whoosh-short mientras viaja el pulso y blip al llegar. En `pull`: whoosh del alejamiento y chime cuando entra el cierre. Funciona muda.\n\n## Limitaciones\n- En la vista final las tarjetas quedan a ~35 % de escala: su texto deja de leerse; se lee el sistema, no el contenido.\n- Onda, miniaturas y cuerpo del email son ilustrativos, no media real.\n- `clipsTag` ("3 clips") y `closing` ("5 piezas") son texto: no se calculan.\n- Cierre sin wrap; t\xEDtulo de la tarjeta 1 sin control de overflow.\n\n## Adaptar a otro tema\n- Webinar B2B: `source = "Webinar Q3"`, `sourceMeta = "1:12:40 \xB7 zoom"`, `quoteTag = "Cita para LinkedIn"`, `mailTag = "Follow-up"`, `mailSubject = "Las 3 m\xE9tricas del webinar"`, `closing = "1 webinar. 5 piezas."`, `theme = "indigo"`.\n- Estudio de yoga: `source = "Clase 12"`, `transcript` con 4 frases de la clase, `clipsTag = "3 reels"`, `quote = "Respirar tambi\xE9n es entrenar."`, `mailSubject = "Tu pr\xE1ctica de esta semana"`, `theme = "sage"`.\n- Inmobiliaria: `source = "Recorrido Casa Lomas"`, `sourceMeta = "18:40 \xB7 video"`, `clipsTag = "3 reels de la casa"`, `mailTag = "Correo a prospectos"`, `closing = "1 recorrido. 5 piezas."`.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Inspiraci\xF3n: thread through modalities \u2190 HowDevelop. Sin media de terceros (no hay `params.media`).\n',
2107
+ "bundle": {
2108
+ "objectKey": "authored-motion/1.0.0/thread-repurpose/eca5a757b5042720176582e07863c959c233d7e19a1993b0a2334956ffa815fe.tar.gz",
2109
+ "sha256": "eca5a757b5042720176582e07863c959c233d7e19a1993b0a2334956ffa815fe",
2110
+ "bytes": 2935731,
2111
+ "fileCount": 58,
2112
+ "contentType": "application/gzip"
2113
+ },
2114
+ "previews": [
2115
+ {
2116
+ "ratio": "wide",
2117
+ "width": 1920,
2118
+ "height": 1080,
2119
+ "duration": 14,
2120
+ "video": {
2121
+ "objectKey": "authored-motion/1.0.0/thread-repurpose/previews/f1b885e159422e8abbee78098eec61f4bf7ba4d8731ab9dedfe047845b6ddc2e.mp4",
2122
+ "sha256": "f1b885e159422e8abbee78098eec61f4bf7ba4d8731ab9dedfe047845b6ddc2e",
2123
+ "bytes": 1193792,
2124
+ "contentType": "video/mp4"
2125
+ },
2126
+ "poster": {
2127
+ "objectKey": "authored-motion/1.0.0/thread-repurpose/previews/90b54646c7e62a46a0424c642ca2199bdbc079a45eebe587e546b4ea13f31a4f.jpg",
2128
+ "sha256": "90b54646c7e62a46a0424c642ca2199bdbc079a45eebe587e546b4ea13f31a4f",
2129
+ "bytes": 62905,
2130
+ "contentType": "image/jpeg"
2131
+ }
2132
+ },
2133
+ {
2134
+ "ratio": "square",
2135
+ "width": 1080,
2136
+ "height": 1080,
2137
+ "duration": 14,
2138
+ "video": {
2139
+ "objectKey": "authored-motion/1.0.0/thread-repurpose/previews/e71d1afd9a931f55ac557036db417e8347954945195e0cfa94da22c590ba0357.mp4",
2140
+ "sha256": "e71d1afd9a931f55ac557036db417e8347954945195e0cfa94da22c590ba0357",
2141
+ "bytes": 922486,
2142
+ "contentType": "video/mp4"
2143
+ },
2144
+ "poster": {
2145
+ "objectKey": "authored-motion/1.0.0/thread-repurpose/previews/b3bf868ad4834fca8865fd894731f79620bac2e507e97cfddfc635b0b344023d.jpg",
2146
+ "sha256": "b3bf868ad4834fca8865fd894731f79620bac2e507e97cfddfc635b0b344023d",
2147
+ "bytes": 36891,
2148
+ "contentType": "image/jpeg"
2149
+ }
2150
+ },
2151
+ {
2152
+ "ratio": "tall",
2153
+ "width": 1080,
2154
+ "height": 1920,
2155
+ "duration": 14,
2156
+ "video": {
2157
+ "objectKey": "authored-motion/1.0.0/thread-repurpose/previews/1b020e84530bfb34bb0aa610e164e9e4b059611dfc2b5b456c4df822ab5cca80.mp4",
2158
+ "sha256": "1b020e84530bfb34bb0aa610e164e9e4b059611dfc2b5b456c4df822ab5cca80",
2159
+ "bytes": 1552469,
2160
+ "contentType": "video/mp4"
2161
+ },
2162
+ "poster": {
2163
+ "objectKey": "authored-motion/1.0.0/thread-repurpose/previews/2172cea11237cc0a35d6efc0f3b514e1341477db6904ed90dd527484270c05bd.jpg",
2164
+ "sha256": "2172cea11237cc0a35d6efc0f3b514e1341477db6904ed90dd527484270c05bd",
2165
+ "bytes": 71021,
2166
+ "contentType": "image/jpeg"
2167
+ }
2168
+ }
2169
+ ],
2170
+ "qa": {
2171
+ "status": "source-and-preview-hashes-verified",
2172
+ "previewCount": 3,
2173
+ "artisticApproval": "not-certified",
2174
+ "audioListened": false,
2175
+ "transferCertified": false
2176
+ },
2177
+ "restrictions": [
2178
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
2179
+ "Scene-specific data/count/layout limits are documented in the selected card.",
2180
+ "Opaque output only; no alpha export.",
2181
+ "Voice/music requires timeScale=1 until explicitly retimed.",
2182
+ "Inspect frame boundaries, typography and final audio before delivery."
2183
+ ]
2184
+ },
2185
+ {
2186
+ "id": "cash-runway",
2187
+ "version": "1.0.0",
2188
+ "title": "Utilidad no es caja",
2189
+ "mechanism": "live-simulation",
2190
+ "intent": "Explicar un sistema con estado en el tiempo; comparar dos reglas sobre la misma simulaci\xF3n.",
2191
+ "niche": "finanzas",
2192
+ "style": "datos",
2193
+ "duration": 14,
2194
+ "runtime": "canvas",
2195
+ "supportedRatios": [
2196
+ "16:9",
2197
+ "1:1",
2198
+ "9:16"
2199
+ ],
2200
+ "sourceSha256": "b1d1c85c2dc054eddaa1d5c25fb321692e5ab83291f0206af95ff77bc5785f1d",
2201
+ "cardSha256": "b3cf4afe825815f36366036c38389222b2cd9e7ae3afb18ce188df0e4750a61e",
2202
+ "card": '# cash-runway \xB7 Utilidad no es caja\n\n**Mecanismo.** Una simulaci\xF3n d\xEDa a d\xEDa, determinista y calculada en `setup`, de caja vs. utilidad acumulada. Tanque de 10 bloques, n\xFAmero grande, bit\xE1cora de eventos y gr\xE1fica son vistas del mismo estado, as\xED que nunca se contradicen. Corrida A (cobro diferido): la utilidad sube mientras la caja cruza a negativo. Corrida B cambia una sola regla (anticipo) y se dibuja sobre el fantasma de A.\n\n**\xDAsala para.** Servicios B2B: cobrar a 60 d\xEDas vs. pedir anticipo. Constructora/manufactura: materiales pagados antes de cobrar. Agencia: proyecto pagado al final vs. por adelantado. Educaci\xF3n financiera para emprendedores: por qu\xE9 una empresa rentable se queda sin caja. **No la uses para.** Mensajes sin estado acumulado en el tiempo. 3+ escenarios. Datos reales con calendario irregular (renta y n\xF3mina son mensuales fijas en c\xF3digo). Casos donde A no llega a negativo: la escena no truena, pero pierde su momento central.\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| runA | 0.9 | Corre el reloj de d\xEDas de A; `headA` arriba; el tanque baja; la bit\xE1cora lista renta, n\xF3mina, material y "venta \xB7 cobro d\xEDa X". |\n| zero | 4.2 | Llega al primer d\xEDa con caja < 0 (calculado) y el reloj se frena 1.4 s; `headZero` en neg; c\xEDrculo neg y "d\xEDa N" en la gr\xE1fica; el tanque parpadea `noCash`. Si A nunca baja de 0, usa el d\xEDa m\xE1s bajo y omite el c\xEDrculo. |\n| runAEnd | 7.6 | A llega al \xFAltimo d\xEDa; el \xE1rea bajo cero queda sombreada en neg. |\n| fix | 8.2 | Cambia a la corrida B: `headB`; la l\xEDnea A queda como fantasma (muted, 35 %); el d\xEDa vuelve a 0. |\n| runB | 8.9 | Corre B de 0 al \xFAltimo d\xEDa. |\n| runBEnd | 11.7 | B termina; `headEnd` en accent. |\n| end | 14 | No lo usa el c\xF3digo. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand, duration, fps, timeScale | kit `terminal` (oscuro), 14 s | \u2014 |\n| currency | prefijo; formato `en-US` (comas), signo "\u2212" | locale fijo |\n| model.days | d\xEDas simulados; eje y reloj se adaptan | 60\u2013120 |\n| model.startCash | caja inicial = 10 bloques del tanque | caja > startCash no se ve (tanque topa) |\n| model.sale / saleEveryDays | monto y cadencia; venta en d\xEDas con `d % saleEveryDays === 3`, nunca en los \xFAltimos 6 d\xEDas | \u2014 |\n| model.materialsShare | fracci\xF3n de la venta pagada como material el d\xEDa de la venta | 0\u20131 |\n| model.payroll / rent | salidas fijas: renta d\xEDas 1, 31, 61\u2026; n\xF3mina d\xEDas 5, 35, 65\u2026 (calendario en c\xF3digo) | \u2014 |\n| model.collectDelayDays | d\xEDas entre venta y cobro | \u2014 |\n| model.upfrontShareFix | fracci\xF3n cobrada el d\xEDa de la venta en B; el resto, a `collectDelayDays` | con 1 la bit\xE1cora muestra filas "cobro" sin monto |\n| copy.headA / headB / headEnd | frases mono arriba; `{every}` = saleEveryDays, `{delay}` = collectDelayDays, `{upfront}` = upfrontShareFix en % (en headA y headB) | 1 l\xEDnea: \u2264 ~80 car. 16:9, ~50 en 1:1, ~45 en 9:16 (ya reemplazado) |\n| copy.headZero | `{day}` d\xEDa del cruce, `{profit}` utilidad ese d\xEDa, `{cash}` caja ese d\xEDa | el texto "caja en negativo" es fijo: reescr\xEDbelo si A no cruza |\n| copy.cashLabel, profitLabel, day, noCash | etiquetas del panel | cortas; `noCash` cabe en el ancho del tanque |\n| copy.sale, collect, upfront, materials, payroll, rent | nombres de eventos en la bit\xE1cora | \u2264 ~20 car. |\n| beats.* | ver tabla | `zero` es cu\xE1ndo se llega al cruce: runA\u2192zero comprime los d\xEDas 0\u2013cruce |\n| sound.cues | 6 cues | \u2014 |\n\nCalculado: d\xEDa de cruce, caja/utilidad diaria de A y B, rango del eje, bit\xE1cora. Con el ejemplo: caja negativa el d\xEDa 31 con utilidad +$34,250.\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- `model` completo (todo se recalcula) y `currency`.\n- Copy: plazo, cadencia y % de anticipo entran solos v\xEDa `{every}/{delay}/{upfront}`; revisa `headEnd` y el texto fijo de `headZero`.\n- Beats con la voz: `zero` en "y aqu\xED te quedas sin caja", `fix` en la soluci\xF3n.\n- Roles: `neg` (negativo, headZero, \xE1rea, c\xEDrculo), `accent` (tanque, l\xEDnea, punto, headEnd), `accent2` (el tanque vira accent\u2192accent2 al vaciarse en A), `pos` (utilidad, montos positivos), `ink`, `muted`, `line`. Fuentes: `mono` (casi todo), `display` (n\xFAmero grande).\n\n## Qu\xE9 requiere tocar c\xF3digo\n- Calendario de renta/n\xF3mina, offset de ventas (d\xEDa 3) y corte de los \xFAltimos 6 d\xEDas.\n- Otra regla de mejora (p. ej. acortar plazo en vez de anticipo) o una tercera corrida.\n- Locale/formato de n\xFAmeros, n\xFAmero de bloques del tanque, wrap de l\xEDneas largas.\n\n## Formatos\n- 16:9: encabezado arriba; tanque a la izquierda con el n\xFAmero al lado; bit\xE1cora a la derecha (desde 55 % W, 8 filas); gr\xE1fica a todo el ancho abajo (300u).\n- 1:1: bit\xE1cora bajo el n\xFAmero (5 filas); gr\xE1fica abajo (340u).\n- 9:16: todo apilado: tanque+n\xFAmero, bit\xE1cora (8 filas), gr\xE1fica grande (560u) en la mitad inferior.\n\n## Sonido\nblip al arrancar A; drop + thud en el cruce a negativo; whoosh-short al cambiar de regla; blip al arrancar B; success al terminar B. Funciona muda: n\xFAmero rojo y "SIN CAJA" cargan el momento.\n\n## Limitaciones\n- Modelo simplificado e ilustrativo (sin impuestos, cr\xE9dito ni calendario real): rotularlo as\xED.\n- Si A nunca baja de 0: usa el d\xEDa m\xE1s bajo, omite el c\xEDrculo y "SIN CAJA", y deja un aviso en `getDiagnostics()`; `headZero` sigue diciendo lo que escribas. Si B tambi\xE9n baja de 0, el mensaje se contradice (no se valida).\n- Mucho texto mono chico (20\u201328u): denso en 9:16 visto en celular.\n\n## Adaptar a otro tema\n- Constructora: `model = {days:120, startCash:300000, sale:120000, saleEveryDays:14, materialsShare:0.35, payroll:60000, rent:15000, collectDelayDays:90, upfrontShareFix:0.5}` (A cruce d\xEDa 45, B nunca baja de $165k), `headA = "facturas por avance \xB7 cobras a {delay} d\xEDas"`, `headB = "mismos avances, con {upfront}% de anticipo"`. Al cambiar cifras, verifica que A cruce y B no.\n- Agencia: `sale = 60000`, `saleEveryDays = 30`, `materialsShare = 0.1`, `collectDelayDays = 45`, `upfrontShareFix = 0.5`, `copy.sale = "proyecto"`, `headA = "cierras un proyecto al mes \xB7 cobras a {delay} d\xEDas"`.\n- Exportador: `currency = "US$"`, `collectDelayDays = 75` (A cruce d\xEDa 31 y cierra en \u2212US$29,000), `copy.collect = "pago del cliente"` (headA por defecto ya dice "cobras a 75 d\xEDas"), `theme = "ledger"`.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Inspiraci\xF3n: live simulation \u2190 ParkerRex token bucket (el encabezado de una frase sigue ese estilo, citado en el c\xF3digo). Sin media de terceros (no hay `params.media`).\n',
2203
+ "bundle": {
2204
+ "objectKey": "authored-motion/1.0.0/cash-runway/dd2f8c2413ad3665ecf1263ebb0faca585c7111e1ac2174d15ffa81ed4560e84.tar.gz",
2205
+ "sha256": "dd2f8c2413ad3665ecf1263ebb0faca585c7111e1ac2174d15ffa81ed4560e84",
2206
+ "bytes": 2936004,
2207
+ "fileCount": 58,
2208
+ "contentType": "application/gzip"
2209
+ },
2210
+ "previews": [
2211
+ {
2212
+ "ratio": "wide",
2213
+ "width": 1920,
2214
+ "height": 1080,
2215
+ "duration": 14,
2216
+ "video": {
2217
+ "objectKey": "authored-motion/1.0.0/cash-runway/previews/c64e638430866e2151753928b581d04e4a6d8b3dd7e1debab1f45b4228ffa715.mp4",
2218
+ "sha256": "c64e638430866e2151753928b581d04e4a6d8b3dd7e1debab1f45b4228ffa715",
2219
+ "bytes": 1079317,
2220
+ "contentType": "video/mp4"
2221
+ },
2222
+ "poster": {
2223
+ "objectKey": "authored-motion/1.0.0/cash-runway/previews/eea583c82252343ba3161b02035ec39ecc8afdf1f5602fc68b300596de509e99.jpg",
2224
+ "sha256": "eea583c82252343ba3161b02035ec39ecc8afdf1f5602fc68b300596de509e99",
2225
+ "bytes": 121695,
2226
+ "contentType": "image/jpeg"
2227
+ }
2228
+ },
2229
+ {
2230
+ "ratio": "square",
2231
+ "width": 1080,
2232
+ "height": 1080,
2233
+ "duration": 14,
2234
+ "video": {
2235
+ "objectKey": "authored-motion/1.0.0/cash-runway/previews/c78f70345b2394fb42568894a33a83d0045e10d57a4881e561305a8ea1a43e4e.mp4",
2236
+ "sha256": "c78f70345b2394fb42568894a33a83d0045e10d57a4881e561305a8ea1a43e4e",
2237
+ "bytes": 780093,
2238
+ "contentType": "video/mp4"
2239
+ },
2240
+ "poster": {
2241
+ "objectKey": "authored-motion/1.0.0/cash-runway/previews/4175a108ebee142d24d96487741fbecefd6ed635c3907f238b1e8d76d94d6a87.jpg",
2242
+ "sha256": "4175a108ebee142d24d96487741fbecefd6ed635c3907f238b1e8d76d94d6a87",
2243
+ "bytes": 82513,
2244
+ "contentType": "image/jpeg"
2245
+ }
2246
+ },
2247
+ {
2248
+ "ratio": "tall",
2249
+ "width": 1080,
2250
+ "height": 1920,
2251
+ "duration": 14,
2252
+ "video": {
2253
+ "objectKey": "authored-motion/1.0.0/cash-runway/previews/68f4b9f29ffc4e4ec6c4579dbd245f924905392a2662ff3bd9ec15714c3cdefb.mp4",
2254
+ "sha256": "68f4b9f29ffc4e4ec6c4579dbd245f924905392a2662ff3bd9ec15714c3cdefb",
2255
+ "bytes": 1117421,
2256
+ "contentType": "video/mp4"
2257
+ },
2258
+ "poster": {
2259
+ "objectKey": "authored-motion/1.0.0/cash-runway/previews/55d82f0fc2b247837c2b8d038f8cb7d9b709e4f65baebed98b0bac8aa35e2583.jpg",
2260
+ "sha256": "55d82f0fc2b247837c2b8d038f8cb7d9b709e4f65baebed98b0bac8aa35e2583",
2261
+ "bytes": 118663,
2262
+ "contentType": "image/jpeg"
2263
+ }
2264
+ }
2265
+ ],
2266
+ "qa": {
2267
+ "status": "source-and-preview-hashes-verified",
2268
+ "previewCount": 3,
2269
+ "artisticApproval": "not-certified",
2270
+ "audioListened": false,
2271
+ "transferCertified": false
2272
+ },
2273
+ "restrictions": [
2274
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
2275
+ "Scene-specific data/count/layout limits are documented in the selected card.",
2276
+ "Opaque output only; no alpha export.",
2277
+ "Voice/music requires timeScale=1 until explicitly retimed.",
2278
+ "Inspect frame boundaries, typography and final audio before delivery."
2279
+ ]
2280
+ },
2281
+ {
2282
+ "id": "zoom-niche",
2283
+ "version": "1.0.0",
2284
+ "title": "De millones a uno",
2285
+ "mechanism": "self-similar-zoom",
2286
+ "intent": "Embudo de segmentaci\xF3n, nichos, ir de lo general a lo espec\xEDfico sin cortes.",
2287
+ "niche": "marketing",
2288
+ "style": "p\xF3ster",
2289
+ "duration": 13,
2290
+ "runtime": "canvas",
2291
+ "supportedRatios": [
2292
+ "16:9",
2293
+ "1:1",
2294
+ "9:16"
2295
+ ],
2296
+ "sourceSha256": "ff95eaf26116986636bd39cd2316a7091ee26324dcd796a05cb315c40c2e48ec",
2297
+ "cardSha256": "900e2e2f9201fa7382bdb126045a2cac7742c0d5cc0448e4dba934d2f759220a",
2298
+ "card": '# zoom-niche \xB7 De millones a uno\n\n**Mecanismo.** Zoom autosimilar sin cortes: cada nivel es un campo de puntos (una poblaci\xF3n); un punto se enciende en accent, la c\xE1mara se zambulle en \xE9l y dentro ya vive el nivel siguiente, que lo reemplaza sin corte. El n\xFAmero grande baja (interpolaci\xF3n logar\xEDtmica) y la etiqueta cambia; en el \xFAltimo nivel la multitud se desvanece y queda una sola figura de persona.\n\n**\xDAsala para.** Marketing/ventas: de mercado total a cliente ideal. Agencia: segmentaci\xF3n de audiencia para ads. Reclutamiento: de postulantes a contratado. Startup: TAM \u2192 SAM \u2192 SOM \u2192 primer cliente. **No la uses para.** Proporciones exactas (los puntos no representan cantidades: la densidad es fija por nivel). Comparar segmentos lado a lado. Embudos donde el n\xFAmero sube o alg\xFAn nivel vale 0.\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| start | 0.5 | Arranca el nivel 0 (visible desde 0 s, fade 0.8 s); `kicker` arriba-izquierda; n\xFAmero y etiqueta del nivel 0 abajo-izquierda. |\n| zoom | 1.15 | (duraci\xF3n) zambullida al punto encendido; el nivel siguiente aparece dentro; el n\xFAmero rueda y la etiqueta cruza a mitad. |\n| hold | 1.35 | (duraci\xF3n) cada nivel se sostiene `hold` s; su punto se enciende a +0.15\u20130.5 s. Ciclo por nivel = hold + zoom = 2.5 s: niveles en 0.5, 3.0, 5.5, 8.0, 10.5. |\n| end | 13 | No lo usa el c\xF3digo. En el \xFAltimo nivel los puntos bajan a 15 % y la persona aparece \u224810.85\u201311.5 s. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand, duration, fps, timeScale | kit `riso`, 13 s | duraci\xF3n \u2248 start + (n\u22121)\xB7(hold+zoom) + 2.5 |\n| thousands | separador de miles del n\xFAmero | `","`, `"."` o `" "` |\n| levels[] | `{count, label}` de mayor a menor | 3\u20136 niveles |\n| levels[].count | n\xFAmero mostrado (dado, no calculado), redondeado | \u2265 1 (log); \u2264 7 d\xEDgitos: "5,400,000" ya toca el borde en 9:16 |\n| levels[].label | frase bajo el n\xFAmero | 1 l\xEDnea sin wrap: \u2264 ~40 car. en 9:16, ~50 en 1:1, ~80 en 16:9 |\n| copy.kicker | frase fija arriba en accent sobre bloque de papel | 1 l\xEDnea corta |\n| beats.start / hold / zoom | arranque y duraciones por nivel | hold \u2265 1 s para leer el n\xFAmero |\n| sound.cues | vac\xEDo: el sonido lo genera `cues(S)` desde `levels` y beats (ver Sonido) | lo que pongas aqu\xED se suma a los generados, no los reemplaza |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- `levels` (cifras, etiquetas, cu\xE1ntos niveles), `thousands`, `kicker`.\n- Beats: alarga `hold` si la voz explica cada filtro y ajusta `duration`; el sonido se regenera solo.\n- Roles: `bg` (papel y paneles), `ink` (puntos, n\xFAmero, etiqueta, contorno de la persona), `accent` (punto encendido, relleno de la persona, kicker). Fuentes: `display` (n\xFAmero con `stretch: expanded` forzado en c\xF3digo, kicker), `text` (etiqueta).\n\n## Qu\xE9 requiere tocar c\xF3digo\n- Densidad y posici\xF3n de puntos (espaciados `[0.17,0.15,0.16,0.14,0.2]` y punto objetivo por nivel, ciclo de 5).\n- \xCDcono final (persona fija): logo, producto o foto requieren c\xF3digo.\n- Quitar el misregistro riso (3u) y el grano, siempre activos.\n- Wrap de etiquetas, n\xFAmero abreviado ("5.4 M").\n- Tipo o volumen de los SFX generados (`cues(S)`).\n\n## Formatos\n- 16:9: centro del zoom al centro; panel n\xFAmero+etiqueta abajo-izquierda (H\u221290u), n\xFAmero 150u.\n- 1:1: igual, panel en H\u221280u, n\xFAmero 120u.\n- 9:16: centro del zoom a 42 % H para dejar sitio al panel (H\u2212170u), n\xFAmero 150u, persona 10 % m\xE1s grande.\n\n## Sonido\nGenerado por `cues(S)`, uno por nivel: pop a +0.2 s del inicio del nivel (se enciende el punto), whoosh 0.1 s antes de cada zambullida, chime a +0.4 s del \xFAltimo nivel (persona). Agregar o quitar niveles, o mover `start/hold/zoom`, mueve el sonido con ellos. Funciona muda.\n\n## Limitaciones\n- Cifras del ejemplo (5.4 M negocios en M\xE9xico, etc.) ilustrativas: c\xEDtalas con fuente o rot\xFAlalas.\n- Los puntos no son proporcionales a las cifras.\n- Ganancias de los SFX generados fijas en c\xF3digo (pop 0.5, whoosh 0.45, chime 0.55); para cambiarlas o silenciarlas hay que tocar `cues(S)`.\n- `stretch: expanded` forzado: con fuentes sin eje de ancho no hace nada; con otras puede ensanchar de m\xE1s.\n- Grano y misregistro dan textura riso aunque cambies de kit.\n\n## Adaptar a otro tema\n- Reclutamiento: `levels = [{count:2400,label:"postulantes"},{count:320,label:"pasan el filtro de CV"},{count:45,label:"aprueban la prueba t\xE9cnica"},{count:8,label:"llegan a la final"},{count:1,label:"es tu contrataci\xF3n"}]`, `kicker = "Contratar es filtrar."`.\n- SaaS B2B Colombia: 4 niveles `[{count:1600000,label:"pymes en Colombia"},{count:85000,label:"facturan electr\xF3nicamente"},{count:6000,label:"tienen 20+ empleados"},{count:1,label:"es tu cuenta ideal"}]`, `duration = 10.5`, `theme = "indigo"`.\n- Gimnasio de barrio: `[{count:1200000,label:"personas en tu ciudad"},{count:90000,label:"viven a 3 km"},{count:7000,label:"entrenan o quieren entrenar"},{count:1,label:"es tu pr\xF3ximo socio"}]`, `kicker = "Vende en tu zona."`, `theme = "sport"`.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Inspiraci\xF3n: infinite zoom \u2190 koldo2k. Sin media de terceros (no hay `params.media`).\n',
2299
+ "bundle": {
2300
+ "objectKey": "authored-motion/1.0.0/zoom-niche/5a9b588cfbe79b14c5c86f674e027fac95086d0ab3f7a2804f16f9d25bc085c1.tar.gz",
2301
+ "sha256": "5a9b588cfbe79b14c5c86f674e027fac95086d0ab3f7a2804f16f9d25bc085c1",
2302
+ "bytes": 2934695,
2303
+ "fileCount": 58,
2304
+ "contentType": "application/gzip"
2305
+ },
2306
+ "previews": [
2307
+ {
2308
+ "ratio": "wide",
2309
+ "width": 1920,
2310
+ "height": 1080,
2311
+ "duration": 13,
2312
+ "video": {
2313
+ "objectKey": "authored-motion/1.0.0/zoom-niche/previews/ebbeb401630687070bb9d4f0e362ae47f943e3efa0ad5038728171cd88c70124.mp4",
2314
+ "sha256": "ebbeb401630687070bb9d4f0e362ae47f943e3efa0ad5038728171cd88c70124",
2315
+ "bytes": 47485517,
2316
+ "contentType": "video/mp4"
2317
+ },
2318
+ "poster": {
2319
+ "objectKey": "authored-motion/1.0.0/zoom-niche/previews/ff09dd22815b1df187d15d47333ad4b6f02ead7745768bdb4413e19da8e0dab0.jpg",
2320
+ "sha256": "ff09dd22815b1df187d15d47333ad4b6f02ead7745768bdb4413e19da8e0dab0",
2321
+ "bytes": 379682,
2322
+ "contentType": "image/jpeg"
2323
+ }
2324
+ },
2325
+ {
2326
+ "ratio": "square",
2327
+ "width": 1080,
2328
+ "height": 1080,
2329
+ "duration": 13,
2330
+ "video": {
2331
+ "objectKey": "authored-motion/1.0.0/zoom-niche/previews/0eaee435eaed7f6503410de2dd670afba5af429c1c7a16f4f2a467e385b9178b.mp4",
2332
+ "sha256": "0eaee435eaed7f6503410de2dd670afba5af429c1c7a16f4f2a467e385b9178b",
2333
+ "bytes": 27127582,
2334
+ "contentType": "video/mp4"
2335
+ },
2336
+ "poster": {
2337
+ "objectKey": "authored-motion/1.0.0/zoom-niche/previews/d2fcfda436aa64536a7dbd7fc8c94e867128dd625a8d6de67d23ad42adc2fabf.jpg",
2338
+ "sha256": "d2fcfda436aa64536a7dbd7fc8c94e867128dd625a8d6de67d23ad42adc2fabf",
2339
+ "bytes": 219256,
2340
+ "contentType": "image/jpeg"
2341
+ }
2342
+ },
2343
+ {
2344
+ "ratio": "tall",
2345
+ "width": 1080,
2346
+ "height": 1920,
2347
+ "duration": 13,
2348
+ "video": {
2349
+ "objectKey": "authored-motion/1.0.0/zoom-niche/previews/ff2d9605c96ed68d5e48f48d4370bb4586fd1cd4058cb30c9ccbe81a39955db6.mp4",
2350
+ "sha256": "ff2d9605c96ed68d5e48f48d4370bb4586fd1cd4058cb30c9ccbe81a39955db6",
2351
+ "bytes": 46948355,
2352
+ "contentType": "video/mp4"
2353
+ },
2354
+ "poster": {
2355
+ "objectKey": "authored-motion/1.0.0/zoom-niche/previews/f17a39bd27daed14df6b8e63702fe337b2371a1a0ae31ef9dc80649c4ef31c26.jpg",
2356
+ "sha256": "f17a39bd27daed14df6b8e63702fe337b2371a1a0ae31ef9dc80649c4ef31c26",
2357
+ "bytes": 379695,
2358
+ "contentType": "image/jpeg"
2359
+ }
2360
+ }
2361
+ ],
2362
+ "qa": {
2363
+ "status": "source-and-preview-hashes-verified",
2364
+ "previewCount": 3,
2365
+ "artisticApproval": "not-certified",
2366
+ "audioListened": false,
2367
+ "transferCertified": false
2368
+ },
2369
+ "restrictions": [
2370
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
2371
+ "Scene-specific data/count/layout limits are documented in the selected card.",
2372
+ "Opaque output only; no alpha export.",
2373
+ "Voice/music requires timeScale=1 until explicitly retimed.",
2374
+ "Inspect frame boundaries, typography and final audio before delivery."
2375
+ ]
2376
+ },
2377
+ {
2378
+ "id": "isotype-reviews",
2379
+ "version": "1.0.0",
2380
+ "title": "De cada 10",
2381
+ "mechanism": "isotype-regroup",
2382
+ "intent": "Proporciones con personas contadas: qui\xE9n hace qu\xE9 y qu\xE9 se pierde.",
2383
+ "niche": "restaurantes",
2384
+ "style": "datos",
2385
+ "duration": 11,
2386
+ "runtime": "canvas",
2387
+ "supportedRatios": [
2388
+ "16:9",
2389
+ "1:1",
2390
+ "9:16"
2391
+ ],
2392
+ "sourceSha256": "c07b2c54bf4b921ea11a1b2181634624d9544a7a5df2e45f050627554a55b6a4",
2393
+ "cardSha256": "82a20ad88eaf74c2d386c3dc9f6e6eafbbf1a2fcaad2ec5d229e461f83b5c681",
2394
+ "card": '# isotype-reviews \xB7 De cada 10\n\n**Mecanismo.** Isotype: las proporciones se cuentan con personas, no con porcentajes. Las mismas figuras cambian con cada hecho: a los lectores se les enciende el tel\xE9fono y los dem\xE1s se aten\xFAan; aparece una calificaci\xF3n; los que se van giran, se ti\xF1en de accent, salen caminando y dejan su contorno punteado como costo visible. Un n\xFAmero grande nombra cada grupo (contado desde los params).\n\n**\xDAsala para.** Restaurante/servicio local: cu\xE1ntos clientes se pierden por rese\xF1as bajas. Cl\xEDnica: pacientes que eligen otra cl\xEDnica por su calificaci\xF3n en Google. Hotel/booking: hu\xE9spedes que descartan por puntaje. App m\xF3vil: usuarios que no instalan por el rating de la tienda. **No la uses para.** Porcentajes con decimales o totales > 10 en 16:9/1:1. M\xE1s de un filtro (solo hay lectores \u2192 los que se van). Series de tiempo. Mensajes donde la tarjeta no sea una calificaci\xF3n con estrella (el "\u2605" es fijo).\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| people | 0.3 | Las `total` figuras aparecen una a una (0.07 s); n\xFAmero = `total` + `copy.intro`. `copy.source` gris al pie desde 0.5 s. |\n| read | 1.9 | Se encienden los tel\xE9fonos (accent) de las primeras `readers` figuras; las dem\xE1s se aten\xFAan a `line`; n\xFAmero = `readers` + `readersLabel`. |\n| rating | 4.0 | Tarjeta "\u2605 {rating}" aparece sobre los lectores. |\n| turn | 4.7 | Los `leavers` se voltean, se ti\xF1en de accent y apagan el tel\xE9fono; n\xFAmero (accent) = cantidad de leavers + `leaversLabel`. |\n| leave | 6.5 | Los leavers caminan a la derecha y se desvanecen (1.2 s); queda su contorno punteado accent; la tarjeta sale a +0.6. |\n| closing | 8.1 | Sale el n\xFAmero; entra `copy.closing` en `display`. |\n| end | 11 | No lo usa el c\xF3digo. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand, duration, fps, timeScale | kit `swiss`, 11 s | \u2014 |\n| total | n\xFAmero de figuras | 16:9: una fila, m\xE1x. 10; 1:1: 5 columnas, m\xE1x. 10; 9:16: 5 columnas, m\xE1x. 15 |\n| readers | las primeras N figuras leen; el resto no | \u2264 total |\n| leavers | \xEDndices base 0 de quienes se van; el n\xFAmero mostrado es `leavers.length` | deben ser < `readers` (no se valida) |\n| rating | texto de la tarjeta; el c\xF3digo antepone "\u2605 " | \u2264 5 car.; no se liga a `leaversLabel` |\n| copy.intro / readersLabel / leaversLabel | etiqueta bajo el n\xFAmero; wrap a 21 % W (16:9) o ancho \xFAtil | \u2264 2 l\xEDneas (~40 car.) |\n| copy.closing | frase final (`display`, wrap); `{leavers}` = cantidad de leavers, `{total}` = total | `copy.intro` sigue siendo texto ("De cada 10\u2026"): actual\xEDzalo si cambia `total` |\n| copy.source | aviso de datos al pie, siempre visible | 1 l\xEDnea |\n| beats.* | ver tabla | `leave` \u2265 turn + 1.2 para ver el giro |\n| sound.cues | 3 tick en `people` (no uno por figura), blip, pop, drop, whoosh, stamp | \u2014 |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- `total`, `readers`, `leavers`, `rating` y todo el copy (`closing` toma las cifras solo; `intro` no).\n- Beats con la voz: `read` en "leen rese\xF1as", `turn` en "se van si\u2026", `closing` en la conclusi\xF3n.\n- Roles: `ink` (figuras, n\xFAmeros), `line` (no lectores), `accent` (pantallas, leavers, contornos, rating, n\xFAmero de leavers), `surface` (tarjeta), `bg` (cuerpo del tel\xE9fono), `muted` (fuente). Fuentes: `heavy` (n\xFAmeros, rating), `text` (etiquetas, fuente), `display` (cierre).\n\n## Qu\xE9 requiere tocar c\xF3digo\n- M\xE1s de 10 figuras en 16:9/1:1, o una tercera categor\xEDa.\n- Otro pictograma (silueta fija con tel\xE9fono) u otra forma de salida.\n- Tarjeta sin "\u2605 " o con otro contenido.\n- Calcular las cifras dentro de `intro`, `readersLabel` o `leaversLabel`.\n\n## Formatos\n- 16:9: n\xFAmero 300u a la izquierda (62 % H); fila de figuras desde 31 % W; cierre en la columna izquierda.\n- 1:1: n\xFAmero 190u arriba-izquierda (y 250u); grilla 5\xD72 de figuras de 190u centrada a 60 % H (la tarjeta de rating queda bajo la etiqueta); cierre arriba.\n- 9:16: n\xFAmero 260u (y 420u); grilla 5\xD7N centrada a 53 % H; cierre arriba.\n\n## Sonido\n3 ticks al aparecer las figuras; blip al encender los tel\xE9fonos; pop con la tarjeta; drop cuando los leavers giran; whoosh al irse; stamp en el cierre. Funciona muda.\n\n## Limitaciones\n- Cifras ilustrativas (lo dice `copy.source`); reemplazar por datos reales con fuente.\n- Los leavers caminan hacia la derecha y pasan por encima de su vecino.\n- El color de los leavers interpola ink\u2192accent (no wipe).\n- `rating` e `intro` no se derivan de los n\xFAmeros.\n\n## Adaptar a otro tema\n- Cl\xEDnica dental: `readers = 8`, `leavers = [0,2,5]`, `rating = "4.2"`, `readersLabel = "buscan opiniones en Google"`, `leaversLabel = "eligen otra si ven menos de 4.5\u2605"`, `closing = "Pierdes {leavers} de {total} antes de la primera cita."`, `theme = "sage"`.\n- Hotel boutique: `readers = 9`, `leavers = [1,2,4,6,7]`, `rating = "7.6"`, `leaversLabel = "descartan hoteles con menos de 8"`, `closing = "Pierdes {leavers} de {total} reservas en la b\xFAsqueda."`, `theme = "estate"`.\n- App m\xF3vil: `readers = 7`, `leavers = [0,3,5]`, `rating = "3.9"`, `readersLabel = "revisan las rese\xF1as de la tienda"`, `closing = "{leavers} de {total} no instalan nunca."`, `theme = "indigo"`.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Mecanismo original (lenguaje Isotype de Otto Neurath como m\xE9todo gr\xE1fico, sin assets externos). Sin media de terceros (no hay `params.media`).\n',
2395
+ "bundle": {
2396
+ "objectKey": "authored-motion/1.0.0/isotype-reviews/e6a9dd14666a0653cccb9f9497d3982a220411edcefb391a4c7e9491503a18b9.tar.gz",
2397
+ "sha256": "e6a9dd14666a0653cccb9f9497d3982a220411edcefb391a4c7e9491503a18b9",
2398
+ "bytes": 2933773,
2399
+ "fileCount": 58,
2400
+ "contentType": "application/gzip"
2401
+ },
2402
+ "previews": [
2403
+ {
2404
+ "ratio": "wide",
2405
+ "width": 1920,
2406
+ "height": 1080,
2407
+ "duration": 11,
2408
+ "video": {
2409
+ "objectKey": "authored-motion/1.0.0/isotype-reviews/previews/01a7c1ed6491d428ce6ac084086c7c870c50ff15dde24964e741097ba99fbb24.mp4",
2410
+ "sha256": "01a7c1ed6491d428ce6ac084086c7c870c50ff15dde24964e741097ba99fbb24",
2411
+ "bytes": 598797,
2412
+ "contentType": "video/mp4"
2413
+ },
2414
+ "poster": {
2415
+ "objectKey": "authored-motion/1.0.0/isotype-reviews/previews/d541b14ab7361809950d4a605035dd7e78b2ad60d61b9bec3d118c1f96b0a556.jpg",
2416
+ "sha256": "d541b14ab7361809950d4a605035dd7e78b2ad60d61b9bec3d118c1f96b0a556",
2417
+ "bytes": 73342,
2418
+ "contentType": "image/jpeg"
2419
+ }
2420
+ },
2421
+ {
2422
+ "ratio": "square",
2423
+ "width": 1080,
2424
+ "height": 1080,
2425
+ "duration": 11,
2426
+ "video": {
2427
+ "objectKey": "authored-motion/1.0.0/isotype-reviews/previews/1a7b0798561d0b59bcde588a9295db4c70276ef4f07b00b73930f7fef975ce7b.mp4",
2428
+ "sha256": "1a7b0798561d0b59bcde588a9295db4c70276ef4f07b00b73930f7fef975ce7b",
2429
+ "bytes": 552821,
2430
+ "contentType": "video/mp4"
2431
+ },
2432
+ "poster": {
2433
+ "objectKey": "authored-motion/1.0.0/isotype-reviews/previews/6b2fc242e0385cd4b71bc923ebdd61d6abcd0d9dbe6d619fa7f4d5e46a545b83.jpg",
2434
+ "sha256": "6b2fc242e0385cd4b71bc923ebdd61d6abcd0d9dbe6d619fa7f4d5e46a545b83",
2435
+ "bytes": 65056,
2436
+ "contentType": "image/jpeg"
2437
+ }
2438
+ },
2439
+ {
2440
+ "ratio": "tall",
2441
+ "width": 1080,
2442
+ "height": 1920,
2443
+ "duration": 11,
2444
+ "video": {
2445
+ "objectKey": "authored-motion/1.0.0/isotype-reviews/previews/17b1f667483a3d610e383612a29e3db8e5f8fa7972ba8a941892570d6c82703d.mp4",
2446
+ "sha256": "17b1f667483a3d610e383612a29e3db8e5f8fa7972ba8a941892570d6c82703d",
2447
+ "bytes": 663741,
2448
+ "contentType": "video/mp4"
2449
+ },
2450
+ "poster": {
2451
+ "objectKey": "authored-motion/1.0.0/isotype-reviews/previews/0702755200d37ef39df3873110fe94cd5a490d93a7a572989546ae63d036d6aa.jpg",
2452
+ "sha256": "0702755200d37ef39df3873110fe94cd5a490d93a7a572989546ae63d036d6aa",
2453
+ "bytes": 85173,
2454
+ "contentType": "image/jpeg"
2455
+ }
2456
+ }
2457
+ ],
2458
+ "qa": {
2459
+ "status": "source-and-preview-hashes-verified",
2460
+ "previewCount": 3,
2461
+ "artisticApproval": "not-certified",
2462
+ "audioListened": false,
2463
+ "transferCertified": false
2464
+ },
2465
+ "restrictions": [
2466
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
2467
+ "Scene-specific data/count/layout limits are documented in the selected card.",
2468
+ "Opaque output only; no alpha export.",
2469
+ "Voice/music requires timeScale=1 until explicitly retimed.",
2470
+ "Inspect frame boundaries, typography and final audio before delivery."
2471
+ ]
2472
+ },
2473
+ {
2474
+ "id": "compound-race",
2475
+ "version": "1.0.0",
2476
+ "title": "Empezar antes gana",
2477
+ "mechanism": "race-with-rescaling-axis",
2478
+ "intent": "Comparar dos trayectorias en el tiempo y descomponer el resultado final.",
2479
+ "niche": "finanzas",
2480
+ "style": "datos",
2481
+ "duration": 13,
2482
+ "runtime": "canvas",
2483
+ "supportedRatios": [
2484
+ "16:9",
2485
+ "1:1",
2486
+ "9:16"
2487
+ ],
2488
+ "sourceSha256": "78cc74f88bec3bc392408f485b14672e78de2d5b74c4674696aff629b6a6b1da",
2489
+ "cardSha256": "a7817332d76b0aada3aa54949ff28658a3730dc1c8bf71e59511e78cb3cfd6a6",
2490
+ "card": '# compound-race \xB7 Empezar antes gana\n\n**Mecanismo.** Dos trayectorias de ahorro con inter\xE9s compuesto mensual corren sobre un eje de edad mientras el eje de valor se reescala sin parar (el l\xEDder siempre cerca del tope), as\xED el crecimiento exponencial se siente en vez de explicarse. Al final cada punta se abre en una barra que separa lo aportado (s\xF3lido) del rendimiento (rayado), y la frase de cierre se calcula con los datos.\n\n**\xDAsala para.** Finanzas personales: empezar a ahorrar antes vs. aportar m\xE1s tarde. Retiro: aportaciones vs. tiempo. Negocio: reinvertir utilidades desde el a\xF1o 0 vs. desde el a\xF1o 3. Educaci\xF3n financiera con disclaimer. **No la uses para.** M\xE1s o menos de 2 trayectorias. Crecimientos lineales (la reescala no aporta). Montos chicos (< ~$150 k: el eje tiene piso fijo y las l\xEDneas se quedan pegadas abajo).\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| axes | 0.1 | Eje y gridlines aparecen (0.7 s); cada ahorrador espera en su edad de inicio con "Nombre \xB7 note". |\n| race | 0.9 | La edad corre de la menor `startAge` a `endAge` (inOutSine); las l\xEDneas crecen, la punta muestra "Nombre \xB7 $valor"; el eje Y se reescala con gridlines redondas. |\n| raceEnd | 7.8 | Termina la carrera en `endAge`. |\n| split | 8.2 | Las l\xEDneas se aten\xFAan; cada punta se vuelve barra: aportado s\xF3lido (1.1 s) + rendimiento rayado (+0.4); nombre, total y aportado (+0.9); leyenda (+1.2). |\n| closing | 9.8 | Frase de cierre calculada arriba. |\n| end | 13 | No lo usa el c\xF3digo. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand, duration, fps, timeScale | kit `ledger`, 13 s | \u2014 |\n| currency | prefijo; abreviado "$4.6 M", "$840 k" | sufijos " M"/" k" fijos |\n| annualRate | tasa anual, capitalizaci\xF3n mensual (r/12) | 0.03\u20130.12 razonable |\n| endAge | edad final | marcas del eje cada 5 desde la menor `startAge` |\n| savers[] | `{name, startAge, monthly, note}` | exactamente 2: con 1 truena; con 3 el cierre y el anti-choque de etiquetas lo ignoran |\n| savers[].note | texto mientras espera su turno | \u2264 ~25 car. |\n| copy.axis / contributed / growth | etiqueta del eje y leyenda | cortas |\n| copy.closing | `{a}` ganador (mayor valor final), `{less}` = aportado del otro \u2212 aportado del ganador, `{more}` = diferencia final | solo se usa si el ganador aport\xF3 menos |\n| copy.closingAlt | cierre alterno cuando el ganador aport\xF3 igual o m\xE1s; `{a}`, `{more}` | no uses `{less}` aqu\xED |\n| copy.footnote | `{rate}` = tasa en % redondeada | siempre visible al pie |\n| beats.* | ver tabla | \u2014 |\n| sound.cues | riser cerca del final de la carrera (race+5.6), whoosh-short + coin en `split`, chime en `closing` | \u2014 |\n\nCalculado: valores mes a mes, totales, aportado, eje, cierre. Con el ejemplo: Ana $4.6 M (aport\xF3 $840 k) vs. Beto $3.8 M (aport\xF3 $1.2 M) \u2192 "Ana aport\xF3 $364 k menos y termin\xF3 con $754 k m\xE1s."\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- `savers`, `annualRate`, `endAge`, `currency`, copy: todas las cifras se recalculan.\n- Beats con la voz: `race` cuando empiezas a narrar, `split` en "\xBFde d\xF3nde sali\xF3 la diferencia?", `closing` en la conclusi\xF3n.\n- Roles: `series[0]` y `series[1]` (l\xEDnea, barra y nombre de cada ahorrador), `line` (gridlines), `ink` (eje, totales, leyenda), `muted` (marcas, notas, footnote), `bg` (mezcla del rendimiento). Fuentes: `mono`, `heavy`, `text`, `display`.\n\n## Qu\xE9 requiere tocar c\xF3digo\n- M\xE1s de 2 series, aportaciones variables o retiros.\n- Piso del eje (150 000) y paso de 5 en el eje de edad.\n- Tiempos de desvanecimiento de las notas (2\u20133.2 s, en segundos, no beats).\n- Colores de la leyenda (usa ink gen\xE9rico, no las series); formato de moneda.\n\n## Formatos\n- 16:9: gr\xE1fica a la izquierda con margen derecho de 560u donde aterrizan las barras; cierre arriba dentro de la gr\xE1fica.\n- 1:1: margen derecho 300u; las barras quedan parcialmente sobre la gr\xE1fica atenuada; cierre sobre la gr\xE1fica.\n- 9:16: gr\xE1fica en la franja media (360u arriba, 640u abajo); en `split` las l\xEDneas bajan 88 % y las barras centradas (base H\u2212250u, hasta 42 % H) se superponen a la gr\xE1fica; cierre arriba (150u).\n\n## Sonido\nriser que crece hacia el final de la carrera; whoosh-short al abrir las barras y coin cuando aparece el rendimiento; chime con la frase. Funciona muda.\n\n## Limitaciones\n- Ilustrativo, no asesor\xEDa financiera: tasa constante, sin inflaci\xF3n, comisiones ni impuestos; el footnote lo dice y debe quedarse.\n- La nota bajo la punta del ahorrador tard\xEDo pr\xE1cticamente no se ve (corte fijo a 3.2 s).\n- Si las curvas se juntan, solo se desplaza la etiqueta del primer ahorrador.\n\n## Adaptar a otro tema\n- Retiro: `savers = [{name:"Luc\xEDa",startAge:22,monthly:1500,note:"desde los 22"},{name:"Diego",startAge:32,monthly:3000,note:"desde los 32, el doble"}]`, `annualRate = 0.07`, `endAge = 65` (Luc\xEDa gana aportando menos).\n- Negocio que reinvierte: `savers = [{name:"Tienda A",startAge:0,monthly:5000,note:"reinvierte desde el a\xF1o 0"},{name:"Tienda B",startAge:3,monthly:7000,note:"desde el a\xF1o 3"}]`, `annualRate = 0.12`, `endAge = 15`, `copy.axis = "a\xF1o"` (A gana aportando $115 k menos).\n- P\xFAblico en d\xF3lares: `currency = "US$"`, `monthly` en USD, `footnote = "Ilustrativo: {rate}% anual. No es asesor\xEDa financiera."`, `theme = "indigo"`.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Mecanismo original. Sin media de terceros (no hay `params.media`).\n',
2491
+ "bundle": {
2492
+ "objectKey": "authored-motion/1.0.0/compound-race/54ca24658f377f1450e12955dffcf3be4232d3cbf9cc23892ecf7c138f4bc351.tar.gz",
2493
+ "sha256": "54ca24658f377f1450e12955dffcf3be4232d3cbf9cc23892ecf7c138f4bc351",
2494
+ "bytes": 2934850,
2495
+ "fileCount": 58,
2496
+ "contentType": "application/gzip"
2497
+ },
2498
+ "previews": [
2499
+ {
2500
+ "ratio": "wide",
2501
+ "width": 1920,
2502
+ "height": 1080,
2503
+ "duration": 13,
2504
+ "video": {
2505
+ "objectKey": "authored-motion/1.0.0/compound-race/previews/e3f151107f823a3d6f96a0297b5ada95c8cf5eaa498a9bd59d66704467f7057c.mp4",
2506
+ "sha256": "e3f151107f823a3d6f96a0297b5ada95c8cf5eaa498a9bd59d66704467f7057c",
2507
+ "bytes": 1623307,
2508
+ "contentType": "video/mp4"
2509
+ },
2510
+ "poster": {
2511
+ "objectKey": "authored-motion/1.0.0/compound-race/previews/c6699d6e98893bb2f0dd4f9b44e35419aea883fbfa14da6dd94e7ff5866a4588.jpg",
2512
+ "sha256": "c6699d6e98893bb2f0dd4f9b44e35419aea883fbfa14da6dd94e7ff5866a4588",
2513
+ "bytes": 140551,
2514
+ "contentType": "image/jpeg"
2515
+ }
2516
+ },
2517
+ {
2518
+ "ratio": "square",
2519
+ "width": 1080,
2520
+ "height": 1080,
2521
+ "duration": 13,
2522
+ "video": {
2523
+ "objectKey": "authored-motion/1.0.0/compound-race/previews/28000e2e8357fd1405f5d172a4a5c0b36f4e07f376b7bef22c76b5b18ce310ea.mp4",
2524
+ "sha256": "28000e2e8357fd1405f5d172a4a5c0b36f4e07f376b7bef22c76b5b18ce310ea",
2525
+ "bytes": 1308731,
2526
+ "contentType": "video/mp4"
2527
+ },
2528
+ "poster": {
2529
+ "objectKey": "authored-motion/1.0.0/compound-race/previews/446f8a1ad73c4f188526a48d83ab5003ca6ede4528f6cd461f5c97c650857d83.jpg",
2530
+ "sha256": "446f8a1ad73c4f188526a48d83ab5003ca6ede4528f6cd461f5c97c650857d83",
2531
+ "bytes": 110215,
2532
+ "contentType": "image/jpeg"
2533
+ }
2534
+ },
2535
+ {
2536
+ "ratio": "tall",
2537
+ "width": 1080,
2538
+ "height": 1920,
2539
+ "duration": 13,
2540
+ "video": {
2541
+ "objectKey": "authored-motion/1.0.0/compound-race/previews/7ce4a15532fbb7297f8bd905bc3639974b6060445f5c2fa4cd50327a4af0a958.mp4",
2542
+ "sha256": "7ce4a15532fbb7297f8bd905bc3639974b6060445f5c2fa4cd50327a4af0a958",
2543
+ "bytes": 1502469,
2544
+ "contentType": "video/mp4"
2545
+ },
2546
+ "poster": {
2547
+ "objectKey": "authored-motion/1.0.0/compound-race/previews/34b41fe64073baf7cfa7d63a38511544e6f50ae054816182fc71cbf08a70c654.jpg",
2548
+ "sha256": "34b41fe64073baf7cfa7d63a38511544e6f50ae054816182fc71cbf08a70c654",
2549
+ "bytes": 144396,
2550
+ "contentType": "image/jpeg"
2551
+ }
2552
+ }
2553
+ ],
2554
+ "qa": {
2555
+ "status": "source-and-preview-hashes-verified",
2556
+ "previewCount": 3,
2557
+ "artisticApproval": "not-certified",
2558
+ "audioListened": false,
2559
+ "transferCertified": false
2560
+ },
2561
+ "restrictions": [
2562
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
2563
+ "Scene-specific data/count/layout limits are documented in the selected card.",
2564
+ "Opaque output only; no alpha export.",
2565
+ "Voice/music requires timeScale=1 until explicitly retimed.",
2566
+ "Inspect frame boundaries, typography and final audio before delivery."
2567
+ ]
2568
+ },
2569
+ {
2570
+ "id": "batch-week",
2571
+ "version": "1.0.0",
2572
+ "title": "Graba en lote",
2573
+ "mechanism": "calendar-merge",
2574
+ "intent": "Productividad: tareas dispersas que se fusionan y liberan tiempo visible.",
2575
+ "niche": "creadores",
2576
+ "style": "calendario",
2577
+ "duration": 12,
2578
+ "runtime": "canvas",
2579
+ "supportedRatios": [
2580
+ "16:9",
2581
+ "1:1",
2582
+ "9:16"
2583
+ ],
2584
+ "sourceSha256": "2b0df89f9cf48e50bf869c74111ced71ada046af089133d71b44c183c4da0a75",
2585
+ "cardSha256": "eb8e66b3c84e195b08cbb8c5fad733d3362d1ef3b6765f89e1ebff6c01d63666",
2586
+ "card": '# batch-week \xB7 Graba en lote\n\n**Mecanismo.** Calendario tipo Tetris: sesiones dispersas, cada una envuelta en su propio montaje y recogida (punteados), vuelan a un solo d\xEDa y se funden en un bloque. Los montajes duplicados se pliegan y los huecos que ocupaban se iluminan como "libre". La ganancia de horas se calcula: (sesiones \u2212 1) \xD7 (setup + teardown).\n\n**\xDAsala para.** Creadores: grabar en lote. Ventas: bloque \xFAnico de llamadas vs. llamadas sueltas. Servicios a domicilio: agrupar visitas en un d\xEDa. Fotograf\xEDa/producci\xF3n: un solo montaje de luces para varias sesiones. **No la uses para.** Tareas sin costo fijo por sesi\xF3n (no hay nada que liberar). M\xE1s de ~6 sesiones (el bloque fusionado no cabe en el d\xEDa). Comparar a dos personas o semanas.\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| grid | 0.1 | La rejilla d\xEDas \xD7 horas aparece (0.6 s). |\n| show | 0.8 | Bloques grises de `busy`; luego cada sesi\xF3n ("Grabar", ink) con su montaje/recogida punteados en accent. |\n| count | 2.6 | Entra `copy.before` arriba. |\n| batch | 4.3 | Montajes/recogidas se pliegan; las sesiones vuelan en arco (desfase 0.18 s) a `batchDay`, una tras otra; la etiqueta del d\xEDa cambia a accent2. |\n| merge | 6.1 | Se funden en un bloque "Grabar \xD7N" con un montaje y una recogida s\xF3lidos (accent); `before` cambia a `after`. |\n| free | 7.2 | Los huecos de montaje/recogida originales se iluminan (accent 22 %) con "libre"; +0.4 entra `gain`. |\n| closing | 8.8 | `closing` abajo con subrayado a mano en accent. |\n| end | 12 | No lo usa el c\xF3digo. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand, duration, fps, timeScale | kit `santi`, 12 s | \u2014 |\n| days | etiquetas de d\xEDas (columnas o filas) | 5\u20137, cortas |\n| hours | `[inicio, fin]` del rango visible | marcas cada 2 h fijas |\n| batchDay | \xEDndice del d\xEDa destino | \u2014 |\n| sessions[] | `{day, start, record}` hora de inicio y duraci\xF3n en horas | 2\u20136; el bloque fusionado empieza en `hours[0] + 2` (fijo) y debe terminar antes de `hours[1]` |\n| setup / teardown | horas de montaje/recogida por sesi\xF3n | dibujados antes/despu\xE9s de cada sesi\xF3n |\n| busy[] | `[d\xEDa, inicio, duraci\xF3n]` compromisos grises | no se valida choque con sesiones ni con el bloque fusionado |\n| copy.record / setup / teardown / free | etiquetas | cortas; "Montar/Recoger/libre" solo se dibujan si el hueco mide > 30u de alto y > 60u de ancho |\n| copy.before / after | frases arriba; en `before`, `{n}` = n\xFAmero de sesiones (todas las ocurrencias) | `after` es texto libre |\n| copy.gain | `{h}` = horas ahorradas calculadas, con coma decimal | corto |\n| copy.closing | frase final subrayada | 1 l\xEDnea |\n| beats.* | ver tabla | \u2014 |\n| sound.cues | 4 tick en `count` (uno por sesi\xF3n, fijo), whoosh, snap, swell, scribble | ajusta los ticks si cambias el n\xFAmero de sesiones |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- Agenda completa: `days`, `hours`, `sessions`, `setup`, `teardown`, `busy`, `batchDay`; "\xD7N" y `{h}` se recalculan.\n- Copy (`before` toma `{n}` solo; revisa que `after` siga siendo cierto); beats con la voz (`batch` en "y si lo grabas todo el martes\u2026").\n- Roles: `line` (rejilla), `ink` (sesiones, d\xEDas, busy al 10 %), `surface` (texto sobre sesiones), `accent` (punteados, bloque fusionado, huecos libres, subrayado), `accent2` (d\xEDa destino, etiquetas, "libre", ganancia), `muted` (horas). Fuentes: `display` (d\xEDas, "libre", ganancia, cierre), `text`, `mono`, `heavy` ("Grabar \xD7N").\n\n## Qu\xE9 requiere tocar c\xF3digo\n- Hora de inicio del bloque fusionado (`hours[0] + 2` fijo) y paso de marcas de hora.\n- Cifras dentro de `after`; varios d\xEDas de lote; sesiones de distinto tipo.\n\n## Formatos\n- 16:9: d\xEDas como columnas (rejilla 200u\u2192W\u2212100u, 230u\u2192980u); frases arriba-izquierda, ganancia arriba-derecha, cierre abajo-izquierda.\n- 1:1: igual con rejilla 920\xD7700u desde y 300u; `gain` va a la izquierda, 75u debajo de la frase.\n- 9:16: d\xEDas como filas y horas en horizontal (rejilla 140u, 420u\u21921620u); frases y ganancia apiladas arriba-izquierda; cierre a H\u2212150u. La recogida de 0.5 h queda demasiado angosta para su etiqueta.\n\n## Sonido\npaper al aparecer la agenda; 4 ticks fijos al contar (uno por sesi\xF3n del ejemplo); whoosh cuando vuelan; snap al fundirse; swell bajo el tiempo liberado; scribble con el subrayado. Funciona muda.\n\n## Limitaciones\n- `after` no se deriva de `sessions`.\n- En 16:9 `gain` comparte l\xEDnea con `before/after` (derecha vs. izquierda): frases de > ~55 car. pueden alcanzarlo.\n- Agenda ilustrativa.\n- Un bloque fusionado que pase de `hours[1]` se sale de la rejilla; un `busy` en el d\xEDa destino puede quedar debajo del bloque.\n- Kit `santi` usa EB Baskerville / EB Avenir Next como fuentes locales de macOS; en otro renderer caen a fallback.\n\n## Adaptar a otro tema\n- Ventas B2B: `copy.record = "Llamadas"`, `setup = 0.5`, `teardown = 0.25`, 5 `sessions` de 1 h, `before = "{n} bloques \xB7 preparas el CRM {n} veces"`, `after = "1 bloque \xB7 preparas 1 vez"`, `closing = "Mismas llamadas. Menos arranques."` (gain = +3 h), `theme = "indigo"`.\n- Fot\xF3grafo de producto: `hours = [8,20]`, `copy.record = "Shooting"`, `setup = 1.5`, `teardown = 1`, 3 `sessions` de 2 h, `copy.setup = "Luces"`, `after = "1 d\xEDa de shooting \xB7 montas luces 1 vez"` (gain = +5 h; el bloque ocupa 10\u201318.5 h: libera esa franja en `busy` del d\xEDa destino).\n- T\xE9cnico a domicilio: `days` lun\u2013vie (quita de `busy` las entradas de los d\xEDas 5\u20136, si no se dibujan fuera de la rejilla), `copy.record = "Visitas"`, `setup = 1` (traslado), `teardown = 0.5`, `free = "libre"`, `closing = "Mismas visitas. Menos tr\xE1fico."`, `theme = "transit"`.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Mecanismo original. Sin media de terceros (no hay `params.media`).\n',
2587
+ "bundle": {
2588
+ "objectKey": "authored-motion/1.0.0/batch-week/ee6d117c30c469831a2ca5c083284f4ebbe78c4a422abac99a54049c5fdf0b1e.tar.gz",
2589
+ "sha256": "ee6d117c30c469831a2ca5c083284f4ebbe78c4a422abac99a54049c5fdf0b1e",
2590
+ "bytes": 2935126,
2591
+ "fileCount": 58,
2592
+ "contentType": "application/gzip"
2593
+ },
2594
+ "previews": [
2595
+ {
2596
+ "ratio": "wide",
2597
+ "width": 1920,
2598
+ "height": 1080,
2599
+ "duration": 12,
2600
+ "video": {
2601
+ "objectKey": "authored-motion/1.0.0/batch-week/previews/65875efd453d06635827592cc6f9d7a33f085baa04da51f8dd13c0bef09fcfd9.mp4",
2602
+ "sha256": "65875efd453d06635827592cc6f9d7a33f085baa04da51f8dd13c0bef09fcfd9",
2603
+ "bytes": 627230,
2604
+ "contentType": "video/mp4"
2605
+ },
2606
+ "poster": {
2607
+ "objectKey": "authored-motion/1.0.0/batch-week/previews/1b920414f8df23cd8cf0ae54208cf8c043c78311e6994e3f726a52799ae61bba.jpg",
2608
+ "sha256": "1b920414f8df23cd8cf0ae54208cf8c043c78311e6994e3f726a52799ae61bba",
2609
+ "bytes": 87144,
2610
+ "contentType": "image/jpeg"
2611
+ }
2612
+ },
2613
+ {
2614
+ "ratio": "square",
2615
+ "width": 1080,
2616
+ "height": 1080,
2617
+ "duration": 12,
2618
+ "video": {
2619
+ "objectKey": "authored-motion/1.0.0/batch-week/previews/5ee03099947dfb0aee39483e577c4c843ec3e6342002c555b774e0dd5ffc6031.mp4",
2620
+ "sha256": "5ee03099947dfb0aee39483e577c4c843ec3e6342002c555b774e0dd5ffc6031",
2621
+ "bytes": 542688,
2622
+ "contentType": "video/mp4"
2623
+ },
2624
+ "poster": {
2625
+ "objectKey": "authored-motion/1.0.0/batch-week/previews/f7fecb3e780d7422d60be4b416357c0b4357e5b35b89d2ae9fa763e5e1648596.jpg",
2626
+ "sha256": "f7fecb3e780d7422d60be4b416357c0b4357e5b35b89d2ae9fa763e5e1648596",
2627
+ "bytes": 70374,
2628
+ "contentType": "image/jpeg"
2629
+ }
2630
+ },
2631
+ {
2632
+ "ratio": "tall",
2633
+ "width": 1080,
2634
+ "height": 1920,
2635
+ "duration": 12,
2636
+ "video": {
2637
+ "objectKey": "authored-motion/1.0.0/batch-week/previews/da05c32e30bb264e6f8f39b67e1764e52a20747913abe85bec1e46dc0cf33db9.mp4",
2638
+ "sha256": "da05c32e30bb264e6f8f39b67e1764e52a20747913abe85bec1e46dc0cf33db9",
2639
+ "bytes": 564088,
2640
+ "contentType": "video/mp4"
2641
+ },
2642
+ "poster": {
2643
+ "objectKey": "authored-motion/1.0.0/batch-week/previews/c1c3f39d1b3eaf26d45723c2cffc9edb60be7f28ba1836eb147c1a01ce724d61.jpg",
2644
+ "sha256": "c1c3f39d1b3eaf26d45723c2cffc9edb60be7f28ba1836eb147c1a01ce724d61",
2645
+ "bytes": 86731,
2646
+ "contentType": "image/jpeg"
2647
+ }
2648
+ }
2649
+ ],
2650
+ "qa": {
2651
+ "status": "source-and-preview-hashes-verified",
2652
+ "previewCount": 3,
2653
+ "artisticApproval": "not-certified",
2654
+ "audioListened": false,
2655
+ "transferCertified": false
2656
+ },
2657
+ "restrictions": [
2658
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
2659
+ "Scene-specific data/count/layout limits are documented in the selected card.",
2660
+ "Opaque output only; no alpha export.",
2661
+ "Voice/music requires timeScale=1 until explicitly retimed.",
2662
+ "Inspect frame boundaries, typography and final audio before delivery."
2663
+ ]
2664
+ },
2665
+ {
2666
+ "id": "exploded-product",
2667
+ "version": "1.0.0",
2668
+ "title": "Por dentro del producto",
2669
+ "mechanism": "3d-section-cutaway",
2670
+ "intent": "Presentar un producto f\xEDsico: capas, materiales y la consecuencia medible (Three.js).",
2671
+ "niche": "producto",
2672
+ "style": "3D",
2673
+ "duration": 13,
2674
+ "runtime": "canvas-threejs",
2675
+ "supportedRatios": [
2676
+ "16:9",
2677
+ "1:1",
2678
+ "9:16"
2679
+ ],
2680
+ "sourceSha256": "82ce5f7bd2e251426871ae467eb487742dc5b1db9c0b74ef5f9c992a91b161c2",
2681
+ "cardSha256": "12600377f39d8b861b3e116f88c24a5d4e4b6fe0e87069b077c5b9a864f24474",
2682
+ "card": '# exploded-product \xB7 Por dentro del producto\n\n**Mecanismo.** Un termo 3D real (Three.js, perfiles `LatheGeometry`) levanta la tapa y abre una cu\xF1a de corte orientada a c\xE1mara (clipping planes), as\xED las capas se ven en secci\xF3n; cada etiqueta cuelga de un punto 3D proyectado sobre la cara del corte. El producto se cierra y la consecuencia se grafica: temperatura vs horas del producto contra una taza, con curvas calculadas (enfriamiento de Newton hacia 20 \xB0C).\n\n**\xDAsala para.** Marca de termos/botellas: "por qu\xE9 sigue caliente 12 h". E-commerce de vaso o lonchera t\xE9rmica cil\xEDndrica: qu\xE9 hace cada capa. Review de producto: "lo que no ves por dentro" con una cifra que cae en el tiempo. **No la uses para.** Productos no cil\xEDndricos (la geometr\xEDa es un termo fijo); software o servicios sin interior f\xEDsico; mensajes sin consecuencia medible en el tiempo (la gr\xE1fica quedar\xEDa decorativa).\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| intro | 0.2 | El cuerpo sube a su sitio y aparece la sombra de contacto (1 s); la pregunta entra 0.4\u20131.0; nombre del producto en mono. |\n| lift | 1.6 | La tapa sube y el sello flota (1 s); la c\xE1mara se aleja y sube un poco. |\n| cut | 3.0 | Se abre una cu\xF1a de ~112\xB0 centrada en la c\xE1mara (1.1 s); la c\xE1mara de vac\xEDo brilla en `accent2` con pulso. |\n| callouts | 4.2 | Sale la pregunta (3.7\u20134.1); 4 etiquetas entran cada 0.8 s, la l\xEDnea gu\xEDa se traza desde su punto 3D. |\n| close | 8.6 | Salen las etiquetas (8.2\u20138.6); la cu\xF1a se cierra (8.4\u20139.1); la tapa baja y asienta con rebote (~9.35). |\n| temp | 9.4 | Entra la gr\xE1fica; las curvas se trazan 9.7\u219212.2 con \xB0C en vivo al final de cada l\xEDnea y contador "N h"; sale el nombre. |\n| end | 13 | Fin de la \xF3rbita lenta de c\xE1mara (dura todo el plano); el trazado termina en `end \u2212 0.8`. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| `theme` / `brand` | kit (`sage`) y overrides de roles/fuentes | ids de lib/themes.js |\n| `duration` `fps` `timeScale` | 13 s, 30 fps; `timeScale` ausente = 1 | si alargas, mueve `end` (\xF3rbita y curvas dependen de \xE9l) |\n| `product.name` | r\xF3tulo mono en may\xFAsculas | 1 l\xEDnea; no se imprime sobre el objeto |\n| `product.body/steel/cap/liquid` | colores hex aut\xE9nticos: pintura, acero, tapa, l\xEDquido | no siguen el tema (a prop\xF3sito) |\n| `copy.question` | pregunta inicial (display) | wrap a 0.38W en 16:9 (~2 l\xEDneas, \u224840 car.); 0.86W en 1:1/9:16 |\n| `copy.callouts[]` | `{part, label, detail}` | exactamente 4; `part` \u2208 `seal outer vacuum inner` (anclas fijas en c\xF3digo; otro valor rompe la escena); label sin wrap: \u226423 car. en 1:1 (\u2248390 px), ~22 en 9:16 (maxW 420u), ~35 en 16:9 |\n| `copy.tempTitle` | t\xEDtulo de la gr\xE1fica | 1 l\xEDnea |\n| `copy.series[]` | `{name, from, to}` \xB0C al inicio y a `hours`; curva calculada | `from` > `to` > 20 (ambiente fijo 20 \xB0C; si no, NaN); eje fijo 15\u2013100 \xB0C; serie 0 = `accent` gruesa, resto `muted`; pensado para 2 |\n| `copy.hours` | horizonte de la gr\xE1fica y del contador | n\xFAmero |\n| `copy.disclaimer` | nota bajo la gr\xE1fica | 1 l\xEDnea, 18u |\n| `beats` / `sound.cues` | ver tabla y Sonido | \u2014 |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- Pregunta, nombre, las 4 etiquetas (texto y qu\xE9 parte se\xF1ala cada una).\n- `product.*` para igualar el color del SKU real.\n- Temperaturas y horas: las curvas y los \xB0C mostrados se recalculan.\n- Voz: `callouts` (cada etiqueta +0.8 s) y `temp`; `close` antes de que la voz pase a la consecuencia.\n- Roles usados: `bg`+`surface` (fondo radial), `ink` (texto, gu\xEDas, sombra), `muted` (detalles, nombre, serie 2, disclaimer), `line` (base de la gr\xE1fica), `accent` (curva del producto), `accent2` (sello, vac\xEDo y su punto). Fuentes: `display`, `text`, `mono`.\n\n## Qu\xE9 requiere tocar c\xF3digo\n- Otra forma de producto, m\xE1s/menos capas o anclas (perfiles y `S.anchors` fijos).\n- M\xE1s de 4 etiquetas o otro espaciado (posici\xF3n usa `i/3`).\n- Otra m\xE9trica distinta a enfriamiento hacia 20 \xB0C, otra unidad (`\xB0C`, `h` fijos) u otro rango de eje.\n- \xC1ngulo de la cu\xF1a (0.62\u03C0), \xF3rbita, FOV; textura/logo sobre el cuerpo.\n\n## Formatos\n- 16:9 \u2014 producto desplazado a la izquierda (view offset), FOV 26; pregunta a la derecha (0.58W, 0.4H); etiquetas en columna a 0.6W; gr\xE1fica a la derecha (0.34W \xD7 0.42H).\n- 1:1 \u2014 producto algo a la izquierda, FOV 30; pregunta arriba-izquierda; etiquetas en columna a 0.62W (texto al l\xEDmite); gr\xE1fica a la derecha (0.38W \xD7 0.36H).\n- 9:16 \u2014 producto centrado, FOV 34; pregunta arriba-izquierda; etiquetas alternan borde izquierdo/derecho; la gr\xE1fica es una franja arriba (0.8W \xD7 0.13H).\n\n## Sonido\n`whoosh-short` en `lift` (tapa), `whoosh` en `cut` (se abre la secci\xF3n), 4 `tick` en `callouts` +0/0.8/1.6/2.4 (uno por etiqueta), `snap` en `close`+0.75 (la tapa asienta), `swell` en `temp` (sostiene la gr\xE1fica). Funciona muda.\n\n## Limitaciones\n- Producto ficticio (Termo Ruta) y valores ilustrativos; la curva es un modelo plausible, no una medici\xF3n.\n- Bebidas fr\xEDas no caben: `to` < 20 da NaN y valores < 15 \xB0C salen del eje.\n- Render ~3\xD7 m\xE1s lento: ~192 s por formato (390 frames) contra 35\u2013110 s de escenas 2D (out/render-report.json); requiere WebGL.\n- Etiquetas sin wrap (overflow solo como warning); en 9:16 las gu\xEDas cruzan sobre el producto.\n\n## Adaptar a otro tema\n- Vaso para caf\xE9 de especialidad (`theme: cafe`): `product.*` colores del vaso, `product.name`, `copy.question` "\xBFPor qu\xE9 tu latte sigue tibio a las 3 h?", textos de `copy.callouts`, `copy.series` [{Vaso, 85, 60}, {Desechable, 85, 25}], `copy.hours` 3.\n- Lonchera t\xE9rmica cil\xEDndrica (`theme: trattoria`): `copy.question` "\xBFC\xF3mo llega caliente tu comida al mediod\xEDa?", `copy.series` [{Termo, 75, 62}, {Tupper, 75, 28}], `copy.hours` 6, `copy.disclaimer`.\n- Termo para senderismo (`brand.colors` del cliente): `product.body` verde oliva, `copy.question` "\xBFSopa caliente en la cima?", `copy.hours` 8, beats `callouts`/`temp` sincronizados a la voz.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas + Three.js vendorizado en `lib/vendor`, licencia MIT, cargado con import map; addon `RoomEnvironment`). Protagonista con anotaciones ancladas \u2190 ik_builds. Sin media externa.\n',
2683
+ "bundle": {
2684
+ "objectKey": "authored-motion/1.0.0/exploded-product/afe872948cc1ed26a64b93f2a714d11e284c3ce407fa1bf34708d7a918a29911.tar.gz",
2685
+ "sha256": "afe872948cc1ed26a64b93f2a714d11e284c3ce407fa1bf34708d7a918a29911",
2686
+ "bytes": 2936747,
2687
+ "fileCount": 58,
2688
+ "contentType": "application/gzip"
2689
+ },
2690
+ "previews": [
2691
+ {
2692
+ "ratio": "wide",
2693
+ "width": 1920,
2694
+ "height": 1080,
2695
+ "duration": 13,
2696
+ "video": {
2697
+ "objectKey": "authored-motion/1.0.0/exploded-product/previews/64b9e62b32a11b2ec75244cfe9257b833ab490af79413bbba640d04605b2e66b.mp4",
2698
+ "sha256": "64b9e62b32a11b2ec75244cfe9257b833ab490af79413bbba640d04605b2e66b",
2699
+ "bytes": 1254572,
2700
+ "contentType": "video/mp4"
2701
+ },
2702
+ "poster": {
2703
+ "objectKey": "authored-motion/1.0.0/exploded-product/previews/07320b051cbe20eb030a967bdb9e16ae55431576054d8f5db9aa3592d96dab6e.jpg",
2704
+ "sha256": "07320b051cbe20eb030a967bdb9e16ae55431576054d8f5db9aa3592d96dab6e",
2705
+ "bytes": 58418,
2706
+ "contentType": "image/jpeg"
2707
+ }
2708
+ },
2709
+ {
2710
+ "ratio": "square",
2711
+ "width": 1080,
2712
+ "height": 1080,
2713
+ "duration": 13,
2714
+ "video": {
2715
+ "objectKey": "authored-motion/1.0.0/exploded-product/previews/e295cbfe0a29e9e1c5325b445e02d9c617ca8e2b17516a008e27bc5d9819ed28.mp4",
2716
+ "sha256": "e295cbfe0a29e9e1c5325b445e02d9c617ca8e2b17516a008e27bc5d9819ed28",
2717
+ "bytes": 1023747,
2718
+ "contentType": "video/mp4"
2719
+ },
2720
+ "poster": {
2721
+ "objectKey": "authored-motion/1.0.0/exploded-product/previews/a3bc5adaca77af6954c2568854d3e9fc9a48b5d05c43b60c3be3f344ddb0a05e.jpg",
2722
+ "sha256": "a3bc5adaca77af6954c2568854d3e9fc9a48b5d05c43b60c3be3f344ddb0a05e",
2723
+ "bytes": 43914,
2724
+ "contentType": "image/jpeg"
2725
+ }
2726
+ },
2727
+ {
2728
+ "ratio": "tall",
2729
+ "width": 1080,
2730
+ "height": 1920,
2731
+ "duration": 13,
2732
+ "video": {
2733
+ "objectKey": "authored-motion/1.0.0/exploded-product/previews/0d038d98848e5184cf05333c55180e2a3c9f617bf3c16c60fb181ce016fb173e.mp4",
2734
+ "sha256": "0d038d98848e5184cf05333c55180e2a3c9f617bf3c16c60fb181ce016fb173e",
2735
+ "bytes": 1312312,
2736
+ "contentType": "video/mp4"
2737
+ },
2738
+ "poster": {
2739
+ "objectKey": "authored-motion/1.0.0/exploded-product/previews/338d6fce55f5a7ec0494ccdd547f0e0f617c2b8c55169644aa05c554a0c92e06.jpg",
2740
+ "sha256": "338d6fce55f5a7ec0494ccdd547f0e0f617c2b8c55169644aa05c554a0c92e06",
2741
+ "bytes": 61711,
2742
+ "contentType": "image/jpeg"
2743
+ }
2744
+ }
2745
+ ],
2746
+ "qa": {
2747
+ "status": "source-and-preview-hashes-verified",
2748
+ "previewCount": 3,
2749
+ "artisticApproval": "not-certified",
2750
+ "audioListened": false,
2751
+ "transferCertified": false
2752
+ },
2753
+ "restrictions": [
2754
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
2755
+ "Scene-specific data/count/layout limits are documented in the selected card.",
2756
+ "Opaque output only; no alpha export.",
2757
+ "Voice/music requires timeScale=1 until explicitly retimed.",
2758
+ "Inspect frame boundaries, typography and final audio before delivery."
2759
+ ]
2760
+ },
2761
+ {
2762
+ "id": "strike-replace",
2763
+ "version": "1.0.0",
2764
+ "title": "Tacha y corrige",
2765
+ "mechanism": "editorial-correction-flip",
2766
+ "intent": "Momento tipogr\xE1fico: reemplazar una creencia por otra con reflujo f\xEDsico de palabras.",
2767
+ "niche": "creadores",
2768
+ "style": "tipograf\xEDa",
2769
+ "duration": 8,
2770
+ "runtime": "canvas",
2771
+ "supportedRatios": [
2772
+ "16:9",
2773
+ "1:1",
2774
+ "9:16"
2775
+ ],
2776
+ "sourceSha256": "7ab3ea888e31e51eaadeb7b9b93360bb74ce82dc250c4a490edea085a3791719",
2777
+ "cardSha256": "bd3d8be949fbfed85847da2237eb7c39eb921c01f002516af893ae8f54b35314",
2778
+ "card": '# strike-replace \xB7 Tacha y corrige\n\n**Mecanismo.** Una creencia escrita como frase grande; las palabras equivocadas se tachan a mano y caen fuera de la l\xEDnea con gravedad y giro. Las palabras que sobreviven conservan su identidad y viajan a su sitio en la frase nueva (FLIP entre dos layouts medidos; si cambia la may\xFAscula, el glifo gira). Las palabras nuevas entran empujando desde la derecha y la de \xE9nfasis se subraya: la correcci\xF3n se arma con piezas de la frase vieja.\n\n**\xDAsala para.** Creador/coach: "No necesitas m\xE1s seguidores \u2192 Necesitas m\xE1s clientes". Nutrici\xF3n: "No es comer menos \u2192 Es comer mejor". SaaS B2B: "M\xE1s funciones venden m\xE1s \u2192 Menos fricci\xF3n vende m\xE1s". Finanzas personales: replantear un h\xE1bito en una frase. **No la uses para.** Frases sin palabras compartidas (no hay reflujo: es un cambio de slide); listas, datos o cifras; textos de m\xE1s de ~2 l\xEDneas.\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| in | 0.2 | Las palabras de `before` suben en cascada (0.12 s entre palabras, 0.5 s cada una). |\n| strike | 1.7 | Marcador `accent` tacha cada palabra de `strike` (0.35 s, +0.3 s por palabra); la palabra pasa de `ink` a `muted`. |\n| fall | 2.55 | Las tachadas caen con gravedad, giran y se desvanecen; las sobrevivientes viajan a su nuevo sitio (2.6\u21923.1). |\n| enter | 2.95 | Palabras nuevas entran desde la derecha con rebote (+0.1 s c/u); cambio de may\xFAscula con giro de glifo (3.05\u20133.45). |\n| underline | 3.9 | Subrayado a mano `accent` bajo `emphasis` (0.6 s). |\n| caption | 4.6 | Caption en `muted` bajo la \xFAltima l\xEDnea (0.6 s). |\n| end | 8 | No lo usa el draw; estado final sostenido ~2.8 s. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| `theme` / `brand` | kit (`santi`) y overrides | \u2014 |\n| `duration` `fps` `timeScale` | 8 s, 30 fps, 1 | \u2014 |\n| `before` | frase inicial (display) | se parte por espacios simples; wrap autom\xE1tico a 0.8W (16:9) / 0.84W; 1\u20132 l\xEDneas |\n| `after` | frase final | \xEDdem; toda palabra sin pareja cuenta como nueva |\n| `strike[]` | palabras de `before` que se tachan y caen | coincidencia exacta (may\xFAsculas y puntuaci\xF3n: `seguidores.` \u2260 `seguidores`); \u22643 con beats por defecto (la 4\xAA a\xFAn se tachar\xEDa al empezar a caer) |\n| `emphasis` | palabra nueva pintada `accent2` + subrayado | coincidencia exacta con una palabra *nueva*; si es sobreviviente no tiene efecto |\n| `caption` | subt\xEDtulo (text) | wrap a 0.5W / 0.76W / 0.8W; 1\u20132 l\xEDneas |\n| `beats` / `sound.cues` | ver tabla y Sonido | \u2014 |\nCalculado (no es param): layouts, emparejamiento de sobrevivientes (sin distinguir may\xFAsculas, en orden), posiciones de viaje.\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- `before`, `after`, `strike`, `emphasis`, `caption`.\n- Voz: `strike` en la palabra equivocada, `enter` en la palabra nueva; `underline`/`caption` despu\xE9s.\n- Roles usados: `ink` (frase), `muted` (tachadas, caption), `accent` (tach\xF3n y subrayado), `accent2` (\xE9nfasis); fondo `bg` liso. Fuentes: `display` (frase), `text` (caption).\n- Un `scribble` por palabra tachada (offsets \u2248 k\xB70.3 desde `strike`).\n\n## Qu\xE9 requiere tocar c\xF3digo\n- Tama\xF1os por formato (140/112/116u) si la frase es larga.\n- \xC9nfasis sobre una sobreviviente o sobre varias palabras.\n- Tachar frases parciales en vez de palabras; otra f\xEDsica de salida.\n- M\xE1s de ~3 tachadas sin reajustar el desfase de 0.3 s.\n\n## Formatos\n- 16:9 \u2014 frase centrada a 0.5H, 140u, l\xEDnea 0.8W; caption 36u a 0.5W.\n- 1:1 \u2014 centrada a 0.48H, 112u, l\xEDnea 0.84W; caption 34u a 0.76W.\n- 9:16 \u2014 centrada a 0.44H, 116u, l\xEDnea 0.84W; caption 40u a 0.8W.\n\n## Sonido\n`scribble` en `strike` y +0.35 (dos tachones = dos palabras en `strike`), `drop` en `fall` (caen), `whoosh-short` en `enter`, `snap` en `enter`+0.42 (aterriza la palabra nueva), `scribble` en `underline`. Funciona muda.\n\n## Limitaciones\n- Toda palabra de `before` que no est\xE9 en `strike` ni en `after` se queda quieta en su sitio viejo y choca con la frase nueva: cada palabra debe tacharse o sobrevivir.\n- Palabras repetidas en `strike` se tachan al mismo tiempo; dobles espacios crean "palabras" vac\xEDas.\n- Kit `santi`: Baskerville/Avenir Next v\xEDa `local()` (fuentes del sistema macOS, no redistribuidas): en otro equipo cae a serif/sans gen\xE9rica y cambian los cortes de l\xEDnea.\n- Con 3+ l\xEDneas la caption baja hacia el borde (se coloca 1.05 tama\xF1os bajo la \xFAltima l\xEDnea).\n\n## Adaptar a otro tema\n- Nutrici\xF3n (`theme: sage`): `before` "No es comer menos.", `after` "Es comer mejor.", `strike` ["No","menos."], `emphasis` "mejor.", `caption` nueva.\n- Finanzas (`theme: ledger`): `before` "Ahorra lo que sobre.", `after` "Ahorra antes de gastar.", `strike` ["lo","que","sobre."], `emphasis` "antes", a\xF1adir un `scribble` en `strike`+0.6.\n- SaaS B2B (`theme: indigo`): `before` "M\xE1s funciones venden m\xE1s.", `after` "Menos fricci\xF3n vende m\xE1s.", `strike` ["M\xE1s","funciones","venden"], `emphasis` "fricci\xF3n".\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Mecanismo original.\n',
2779
+ "bundle": {
2780
+ "objectKey": "authored-motion/1.0.0/strike-replace/5c821573b6eb629b9f60d2aacfea438788edc6f0a311b91de2affe74e7ce498a.tar.gz",
2781
+ "sha256": "5c821573b6eb629b9f60d2aacfea438788edc6f0a311b91de2affe74e7ce498a",
2782
+ "bytes": 2933288,
2783
+ "fileCount": 58,
2784
+ "contentType": "application/gzip"
2785
+ },
2786
+ "previews": [
2787
+ {
2788
+ "ratio": "wide",
2789
+ "width": 1920,
2790
+ "height": 1080,
2791
+ "duration": 8,
2792
+ "video": {
2793
+ "objectKey": "authored-motion/1.0.0/strike-replace/previews/34d761f68595cfa68abb5bb5ecde82c231e8d954098e9b6af595b37773512506.mp4",
2794
+ "sha256": "34d761f68595cfa68abb5bb5ecde82c231e8d954098e9b6af595b37773512506",
2795
+ "bytes": 413634,
2796
+ "contentType": "video/mp4"
2797
+ },
2798
+ "poster": {
2799
+ "objectKey": "authored-motion/1.0.0/strike-replace/previews/b67dc9ee604c6ef0ed7dd450e255935f4d1c08057df36937d25a7a51119685f6.jpg",
2800
+ "sha256": "b67dc9ee604c6ef0ed7dd450e255935f4d1c08057df36937d25a7a51119685f6",
2801
+ "bytes": 57326,
2802
+ "contentType": "image/jpeg"
2803
+ }
2804
+ },
2805
+ {
2806
+ "ratio": "square",
2807
+ "width": 1080,
2808
+ "height": 1080,
2809
+ "duration": 8,
2810
+ "video": {
2811
+ "objectKey": "authored-motion/1.0.0/strike-replace/previews/44d167ac6f3c3d6400f3e1384525b8f5afba9dedf6dd954aa152338c13157bfa.mp4",
2812
+ "sha256": "44d167ac6f3c3d6400f3e1384525b8f5afba9dedf6dd954aa152338c13157bfa",
2813
+ "bytes": 361343,
2814
+ "contentType": "video/mp4"
2815
+ },
2816
+ "poster": {
2817
+ "objectKey": "authored-motion/1.0.0/strike-replace/previews/979716fc58ee9cb4f6b0b83ff92e257e23d138a3829a6b6421de093a177a67e0.jpg",
2818
+ "sha256": "979716fc58ee9cb4f6b0b83ff92e257e23d138a3829a6b6421de093a177a67e0",
2819
+ "bytes": 43590,
2820
+ "contentType": "image/jpeg"
2821
+ }
2822
+ },
2823
+ {
2824
+ "ratio": "tall",
2825
+ "width": 1080,
2826
+ "height": 1920,
2827
+ "duration": 8,
2828
+ "video": {
2829
+ "objectKey": "authored-motion/1.0.0/strike-replace/previews/774719bc7c41ba42368a5fda1b62f95d385136bb09be219bc1685a5f0ee37f72.mp4",
2830
+ "sha256": "774719bc7c41ba42368a5fda1b62f95d385136bb09be219bc1685a5f0ee37f72",
2831
+ "bytes": 351020,
2832
+ "contentType": "video/mp4"
2833
+ },
2834
+ "poster": {
2835
+ "objectKey": "authored-motion/1.0.0/strike-replace/previews/ad5fee476451b2c5e290896b2df2f441f783db69738947a39c2eaecaa51181b7.jpg",
2836
+ "sha256": "ad5fee476451b2c5e290896b2df2f441f783db69738947a39c2eaecaa51181b7",
2837
+ "bytes": 53163,
2838
+ "contentType": "image/jpeg"
2839
+ }
2840
+ }
2841
+ ],
2842
+ "qa": {
2843
+ "status": "source-and-preview-hashes-verified",
2844
+ "previewCount": 3,
2845
+ "artisticApproval": "not-certified",
2846
+ "audioListened": false,
2847
+ "transferCertified": false
2848
+ },
2849
+ "restrictions": [
2850
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
2851
+ "Scene-specific data/count/layout limits are documented in the selected card.",
2852
+ "Opaque output only; no alpha export.",
2853
+ "Voice/music requires timeScale=1 until explicitly retimed.",
2854
+ "Inspect frame boundaries, typography and final audio before delivery."
2855
+ ]
2856
+ },
2857
+ {
2858
+ "id": "fuse-stat",
2859
+ "version": "1.0.0",
2860
+ "title": "La mecha",
2861
+ "mechanism": "fuse-countdown-burst",
2862
+ "intent": "Hook con urgencia: el tiempo visible como mecha y un payoff que ocupa el espacio liberado.",
2863
+ "niche": "creadores",
2864
+ "style": "tipograf\xEDa",
2865
+ "duration": 7.5,
2866
+ "runtime": "canvas",
2867
+ "supportedRatios": [
2868
+ "16:9",
2869
+ "1:1",
2870
+ "9:16"
2871
+ ],
2872
+ "sourceSha256": "234717013a61178574971cad376e4ff0d443f00c2a7b9590e33664a236b4ae5a",
2873
+ "cardSha256": "df07f08adb9ab9b6efa9dce6854776fe766b5bbf6301405fe89582447a89951c",
2874
+ "card": '# fuse-stat \xB7 La mecha\n\n**Mecanismo.** El tiempo se vuelve una mecha: una chispa recorre una cuerda punteada a velocidad constante y su avance *es* la cuenta regresiva; el n\xFAmero late y baja en cada paso. Cuando la chispa llega, el n\xFAmero estalla (flash, sacudida, esquirlas, anillo) y la frase de payoff ocupa el espacio que qued\xF3 libre.\n\n**\xDAsala para.** Hook de creador: "Tienes 3 segundos antes de que deslicen". E-commerce: "Quedan 5 horas de env\xEDo gratis". Webinar/curso: "Faltan 3 d\xEDas para cerrar inscripciones". Fitness: "10 repeticiones m\xE1s que ayer". **No la uses para.** Cifras no enteras o de m\xE1s de 2 d\xEDgitos (el n\xFAmero mide 460\u2013560u); mensajes sin urgencia (la explosi\xF3n se vuelve ruido); temas serios o sensibles (el estallido es agresivo).\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| light | 0.5 | Se enciende la chispa (glow + part\xEDculas) al inicio de la mecha; la cuerda ya entr\xF3 (0\u20130.5) y el n\xFAmero con `pre` y `unit post` (0.1\u20130.6). |\n| burn | 0.9 | La chispa avanza lineal; el n\xFAmero late y baja cada (`boom`\u2212`burn`)/`count` s; en 1 pasa a `accent` y usa `unitOne`. |\n| boom | 3.9 | Estalla: flash `ink` de 0.09 s, sacudida 0.5 s, 46 esquirlas `ink`/`accent`, anillo `accent` que se expande. |\n| payoff | 4.35 | El payoff escala 0.8\u21921 con rebote (0.5 s), centrado en cuadro; la \xFAltima l\xEDnea en `accent`. |\n| end | 7.5 | No lo usa el draw; payoff sostenido ~2.6 s. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| `theme` / `brand` | kit (`night`) y overrides | \u2014 |\n| `duration` `fps` `timeScale` | 7.5 s, 30 fps, 1 | \u2014 |\n| `count` | valor inicial de la cuenta | entero \u22651; cada paso dura (`boom`\u2212`burn`)/`count`: para segundos reales usa `boom = burn + count` |\n| `copy.pre` | l\xEDnea sobre el n\xFAmero (display, `muted`) | 1 l\xEDnea |\n| `copy.unit` / `copy.unitOne` | unidad plural / singular | `unitOne` solo cuando el n\xFAmero es 1 |\n| `copy.post` | sigue a la unidad en la misma l\xEDnea | `unit + " " + post` sin wrap: \u2264 ~32 car. (en 9:16 "segundos antes de que deslicen." \u2248 860 de 972 px) |\n| `copy.payoff` | frase final (heavy) | wrap a 0.86W; 2\u20133 l\xEDneas m\xE1x. (140/120/130u) |\n| `beats` / `sound.cues` | ver tabla y Sonido | \u2014 |\nCalculado: n\xFAmero mostrado por paso, ruta de la mecha (Catmull-Rom por formato), progreso de la chispa.\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- `count`, `pre`, `unit`, `unitOne`, `post`, `payoff`.\n- Voz: `burn` cuando empieza la cuenta, `boom` en el golpe, `payoff` en la frase final.\n- Roles usados: `ink` (cuerda, n\xFAmero, flash, esquirlas, payoff), `muted` (ceniza, `pre`/`post`), `accent` (n\xFAmero en 1, esquirlas, anillo, \xFAltima l\xEDnea del payoff), `accent2` (n\xFAcleo de la chispa). Fuentes: `heavy` (n\xFAmero, payoff), `display` (`pre`/`post`).\n- Ticks: uno por paso; offsets = k\xB7(`boom`\u2212`burn`)/`count`.\n\n## Qu\xE9 requiere tocar c\xF3digo\n- Ruta de la mecha (puntos de control fijos por formato).\n- Formato del n\xFAmero (decimales, separadores, unidad pegada tipo "3h").\n- Payoff fuera del centro o alineado al n\xFAmero; explosi\xF3n sin flash o m\xE1s suave.\n\n## Formatos\n- 16:9 \u2014 n\xFAmero en (0.62W, 0.55H), 520u; la mecha entra por abajo-izquierda (0.78H) en S.\n- 1:1 \u2014 n\xFAmero en (0.58W, 0.56H), 460u; mecha equivalente.\n- 9:16 \u2014 n\xFAmero centrado, 560u; la mecha entra arriba-izquierda (0.1H) y serpentea hacia abajo. En los tres, el payoff va centrado en cuadro.\n\n## Sonido\n`snap` en `light` (encendido), 3 `tick` en `burn` +0/1/2 (cada latido del n\xFAmero), `thud` en `boom` + `whoosh` +0.05 (estallido), `stamp` en `payoff`+0.3 (la frase se asienta). Funciona muda.\n\n## Limitaciones\n- Si cambias `count` sin mover `boom`, los pasos dejan de ser segundos reales (con `count` 5 duran 0.6 s).\n- El flash usa `ink`: blanco en kits oscuros, pero en kits claros el cuadro se oscurece 0.09 s (parpadeo).\n- `unit post` sin wrap: overflow solo como warning.\n- La cifra es afirmaci\xF3n del guion, no dato calculado.\n\n## Adaptar a otro tema\n- E-commerce (`theme: trattoria`): `count` 5, `pre` "Quedan", `unit` "horas", `unitOne` "hora", `post` "de env\xEDo gratis.", `payoff` "Pide antes de las 6."; a\xF1adir 2 `tick` (offsets 0, 0.6, 1.2, 1.8, 2.4).\n- Curso/webinar (`theme: indigo`): `count` 3, `pre` "Faltan", `unit` "d\xEDas", `unitOne` "d\xEDa", `post` "para inscribirte.", `payoff` "Aparta tu lugar hoy.".\n- Gimnasio (`theme: sport`): `count` 10, `pre` "Haz", `unit` "repeticiones", `unitOne` "repetici\xF3n", `post` "m\xE1s que ayer.", `payoff` "La \xFAltima es la que cuenta.", `boom` 3.9 (pasos de 0.3 s) y ticks a cada 0.3 s.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Mecanismo original.\n',
2875
+ "bundle": {
2876
+ "objectKey": "authored-motion/1.0.0/fuse-stat/d6db430373d4d7a8bb11aa23e79b17e48385b44b0e644ee42160f0b4413b7e7a.tar.gz",
2877
+ "sha256": "d6db430373d4d7a8bb11aa23e79b17e48385b44b0e644ee42160f0b4413b7e7a",
2878
+ "bytes": 2933378,
2879
+ "fileCount": 58,
2880
+ "contentType": "application/gzip"
2881
+ },
2882
+ "previews": [
2883
+ {
2884
+ "ratio": "wide",
2885
+ "width": 1920,
2886
+ "height": 1080,
2887
+ "duration": 7.5,
2888
+ "video": {
2889
+ "objectKey": "authored-motion/1.0.0/fuse-stat/previews/f0d626e70e496131c03f9f8a105bbe088ef9999cd7f9eade43d774e8ecf0358d.mp4",
2890
+ "sha256": "f0d626e70e496131c03f9f8a105bbe088ef9999cd7f9eade43d774e8ecf0358d",
2891
+ "bytes": 713753,
2892
+ "contentType": "video/mp4"
2893
+ },
2894
+ "poster": {
2895
+ "objectKey": "authored-motion/1.0.0/fuse-stat/previews/d7d7eb0b0a5c0284dafb2cce96acbbd630cd7a854fd51950f49f4ee13653441c.jpg",
2896
+ "sha256": "d7d7eb0b0a5c0284dafb2cce96acbbd630cd7a854fd51950f49f4ee13653441c",
2897
+ "bytes": 48099,
2898
+ "contentType": "image/jpeg"
2899
+ }
2900
+ },
2901
+ {
2902
+ "ratio": "square",
2903
+ "width": 1080,
2904
+ "height": 1080,
2905
+ "duration": 7.5,
2906
+ "video": {
2907
+ "objectKey": "authored-motion/1.0.0/fuse-stat/previews/ade315d376fd9ab2260a8ccfed4eefc9225f690e3d721b4391cb0ee216f1c240.mp4",
2908
+ "sha256": "ade315d376fd9ab2260a8ccfed4eefc9225f690e3d721b4391cb0ee216f1c240",
2909
+ "bytes": 560463,
2910
+ "contentType": "video/mp4"
2911
+ },
2912
+ "poster": {
2913
+ "objectKey": "authored-motion/1.0.0/fuse-stat/previews/a462a223c0f7c598aa6a67bbd0fd78e9ea390e990d95ee6b4642f59c935865bf.jpg",
2914
+ "sha256": "a462a223c0f7c598aa6a67bbd0fd78e9ea390e990d95ee6b4642f59c935865bf",
2915
+ "bytes": 35024,
2916
+ "contentType": "image/jpeg"
2917
+ }
2918
+ },
2919
+ {
2920
+ "ratio": "tall",
2921
+ "width": 1080,
2922
+ "height": 1920,
2923
+ "duration": 7.5,
2924
+ "video": {
2925
+ "objectKey": "authored-motion/1.0.0/fuse-stat/previews/2c01b713477761e26406aaeb87a21ee0ef13c8df030eb3d9f0f66c88a8aab638.mp4",
2926
+ "sha256": "2c01b713477761e26406aaeb87a21ee0ef13c8df030eb3d9f0f66c88a8aab638",
2927
+ "bytes": 666925,
2928
+ "contentType": "video/mp4"
2929
+ },
2930
+ "poster": {
2931
+ "objectKey": "authored-motion/1.0.0/fuse-stat/previews/827a76ed156724e90b224302d273dd566c8f1299fb40ab47ea3b52978f5e5b5e.jpg",
2932
+ "sha256": "827a76ed156724e90b224302d273dd566c8f1299fb40ab47ea3b52978f5e5b5e",
2933
+ "bytes": 44317,
2934
+ "contentType": "image/jpeg"
2935
+ }
2936
+ }
2937
+ ],
2938
+ "qa": {
2939
+ "status": "source-and-preview-hashes-verified",
2940
+ "previewCount": 3,
2941
+ "artisticApproval": "not-certified",
2942
+ "audioListened": false,
2943
+ "transferCertified": false
2944
+ },
2945
+ "restrictions": [
2946
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
2947
+ "Scene-specific data/count/layout limits are documented in the selected card.",
2948
+ "Opaque output only; no alpha export.",
2949
+ "Voice/music requires timeScale=1 until explicitly retimed.",
2950
+ "Inspect frame boundaries, typography and final audio before delivery."
2951
+ ]
2952
+ },
2953
+ {
2954
+ "id": "shape-bridge",
2955
+ "version": "1.0.0",
2956
+ "title": "Puente de forma",
2957
+ "mechanism": "match-cut-object",
2958
+ "intent": "Transici\xF3n entre ideas: un c\xEDrculo persiste y cambia de significado (reloj \u2192 moneda \u2192 sol).",
2959
+ "niche": "transici\xF3n",
2960
+ "style": "p\xF3ster",
2961
+ "duration": 7,
2962
+ "runtime": "canvas",
2963
+ "supportedRatios": [
2964
+ "16:9",
2965
+ "1:1",
2966
+ "9:16"
2967
+ ],
2968
+ "sourceSha256": "068f132ca20ae34e05f5f74cb9a0f1d09328de0da798cddb2b962c390e05e3d7",
2969
+ "cardSha256": "1f4962fedcb0c825e79f77d091a5f4ce8790152542bf0ae947e94e565417134b",
2970
+ "card": '# shape-bridge \xB7 Puente de forma\n\n**Mecanismo.** Un solo c\xEDrculo persiste en el mismo sitio mientras cambia de significado: reloj cuyas manecillas aceleran hasta volverse estela \u2192 moneda que gira de canto tres veces \u2192 la moneda cae tras un horizonte y sale como sol. Cada cambio ocurre en el pico de un gesto del propio objeto; el color de fondo cambia con una barrida circular que nace del c\xEDrculo (o del horizonte), y la palabra cambia con el gesto.\n\n**\xDAsala para.** Productividad/finanzas: "tu TIEMPO \u2192 tu DINERO \u2192 tu LIBERTAD". Freelance/consultor\xEDa: pasar de cobrar por hora a cobrar por valor. Retiro/inversi\xF3n: "HOY \u2192 AHORRO \u2192 RETIRO". Transici\xF3n entre secciones de un video largo sobre tres palabras de la voz. **No la uses para.** Ideas que no encajan en reloj/moneda/sol (los dibujos son fijos); explicar o mostrar datos (no hay cifras); palabras largas o frases.\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| clock | 0.2 | El reloj entra escalando con rebote (0.5 s); palabra 1 a 0.5; las manecillas aceleran (curva ^2.4) con estela desde 1.2. |\n| cut1 | 1.9 | Barrida circular del campo 2 desde el centro (1.85\u20132.2); en 2.2 el reloj es moneda con `$` que gira de canto 3 veces (2.2\u20132.9); palabra 1 sale, palabra 2 entra a 2.25. |\n| cut2 | 3.7 | La moneda cae hasta el horizonte (3.7\u20134.2); barrida del campo 3 desde el horizonte (3.85\u20134.2); l\xEDnea de horizonte + 4 franjas (4.1\u20134.7); el sol sube (4.2\u20135.4); palabra 3 a 4.3. |\n| end | 7 | No lo usa el draw; sol quieto ~1.6 s. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| `theme` / `brand` | kit (`signal`) y overrides | \u2014 |\n| `duration` `fps` `timeScale` | 7 s, 30 fps, 1 | alargar solo extiende el sol quieto |\n| `words[]` | palabra de cada estado (reloj, moneda, sol) | exactamente 3 (tiempos indexados 0\u20132); heavy 190/150/170u, 1 l\xEDnea sin wrap: \u2264 ~11 car. en may\xFAsculas (LIBERTAD \u2248 690 de 924 px en 16:9) |\n| `fieldRoles[]` | rol de color del fondo en cada estado (`bg`, `ink`, `accent`) | 3 nombres de rol; deben contrastar entre s\xED o la barrida no se lee |\n| `objectRole` | rol de color de moneda y sol (`bg`) | si su contraste con el campo es \u22641.6 se sustituye por `bg`/`ink` autom\xE1ticamente |\n| `beats` / `sound.cues` | ver tabla y Sonido | \u2014 |\nCalculado: color de reloj, horizonte y palabras = el m\xE1s contrastado entre `ink`, `bg` y blanco sobre el campo actual.\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- `words`, `fieldRoles`, `objectRole`, tema o `brand.colors`.\n- Voz: `cut1` y `cut2` justo antes de las palabras 2 y 3 (el campo cambia 0.3 s y 0.5 s despu\xE9s del beat).\n- Roles usados: los tres de `fieldRoles` (por defecto `bg`/`ink`/`accent`), `objectRole` (`bg`), y `ink`/`bg` como candidatos de contraste. Fuente: `heavy` (palabras y `$`).\n- Sonido (ticks del reloj, moneda, swell).\n\n## Qu\xE9 requiere tocar c\xF3digo\n- Otros objetos o met\xE1foras (reloj, moneda y sol est\xE1n dibujados en c\xF3digo).\n- S\xEDmbolo de moneda distinto de `$` (fijo).\n- M\xE1s o menos de 3 estados; palabras en varias l\xEDneas o en otra posici\xF3n.\n\n## Formatos\n- 16:9 \u2014 c\xEDrculo a la derecha (0.7W, 0.46H), R 250u; palabras alineadas a la izquierda (x 110u) a media altura.\n- 1:1 \u2014 c\xEDrculo centrado (0.5W, 0.4H), R 230u; palabras centradas a 0.88H.\n- 9:16 \u2014 c\xEDrculo (0.5W, 0.38H), R 270u; palabras centradas a 0.84H.\n\n## Sonido\n3 `tick` en `clock` +0.5/0.9/1.25 (reloj acelerando), `coin` en `cut1`+0.3 (aparece la moneda), `whoosh` en `cut2` (cae), `swell` en `cut2`+0.6 (sale el sol). Funciona muda.\n\n## Limitaciones\n- El `$` de la moneda est\xE1 fijo en c\xF3digo: no sirve tal cual para euros u otras monedas.\n- Palabras sin wrap; overflow solo como warning.\n- En kits donde los roles elegidos se parecen (p.ej. `bg` y `surface` en `night`) la barrida casi no se ve.\n- Puramente simb\xF3lica: no aporta datos.\n\n## Adaptar a otro tema\n- Freelance (`theme: night`): `words` ["HORAS","VALOR","CALMA"], `fieldRoles` ["bg","accent","accent2"], `objectRole` "bg" (moneda y sol oscuros sobre naranja/amarillo).\n- Retiro/inversi\xF3n (`theme: ledger`): `words` ["HOY","AHORRO","RETIRO"], `fieldRoles` ["bg","accent","ink"], `objectRole` "bg".\n- Bienestar (`theme: sage`): `words` ["PAUSA","INVERSI\xD3N","ENERG\xCDA"], `fieldRoles` ["bg","accent","accent2"], `objectRole` "surface".\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Objeto persistente que cambia de funci\xF3n \u2190 twoclipping; cambio de campo de color en el pico del gesto \u2190 ajith_io (ver PROVENANCE.md de la biblioteca).\n',
2971
+ "bundle": {
2972
+ "objectKey": "authored-motion/1.0.0/shape-bridge/338358d4d397bc37a28820b18faf798ba5defdedb8d74a69a0503eae9f0eaf76.tar.gz",
2973
+ "sha256": "338358d4d397bc37a28820b18faf798ba5defdedb8d74a69a0503eae9f0eaf76",
2974
+ "bytes": 2933204,
2975
+ "fileCount": 58,
2976
+ "contentType": "application/gzip"
2977
+ },
2978
+ "previews": [
2979
+ {
2980
+ "ratio": "wide",
2981
+ "width": 1920,
2982
+ "height": 1080,
2983
+ "duration": 7,
2984
+ "video": {
2985
+ "objectKey": "authored-motion/1.0.0/shape-bridge/previews/563d05013b36a6289698e443311e2e232f295bcf0440fc2dff5386e5e2cb4e3f.mp4",
2986
+ "sha256": "563d05013b36a6289698e443311e2e232f295bcf0440fc2dff5386e5e2cb4e3f",
2987
+ "bytes": 567550,
2988
+ "contentType": "video/mp4"
2989
+ },
2990
+ "poster": {
2991
+ "objectKey": "authored-motion/1.0.0/shape-bridge/previews/3a1550fec69a8086ebf55b2b6dca5d7128e955e609dead1be860b280d91e16cd.jpg",
2992
+ "sha256": "3a1550fec69a8086ebf55b2b6dca5d7128e955e609dead1be860b280d91e16cd",
2993
+ "bytes": 53535,
2994
+ "contentType": "image/jpeg"
2995
+ }
2996
+ },
2997
+ {
2998
+ "ratio": "square",
2999
+ "width": 1080,
3000
+ "height": 1080,
3001
+ "duration": 7,
3002
+ "video": {
3003
+ "objectKey": "authored-motion/1.0.0/shape-bridge/previews/14608954be8d53dc9510baa2ddf9f09daf21a9247f18a05ac0928c7d0321e038.mp4",
3004
+ "sha256": "14608954be8d53dc9510baa2ddf9f09daf21a9247f18a05ac0928c7d0321e038",
3005
+ "bytes": 518757,
3006
+ "contentType": "video/mp4"
3007
+ },
3008
+ "poster": {
3009
+ "objectKey": "authored-motion/1.0.0/shape-bridge/previews/b49aba2fb2d212b21cb32dac595a5e74fdd0883465f07bde3ca62b513a38cee1.jpg",
3010
+ "sha256": "b49aba2fb2d212b21cb32dac595a5e74fdd0883465f07bde3ca62b513a38cee1",
3011
+ "bytes": 37177,
3012
+ "contentType": "image/jpeg"
3013
+ }
3014
+ },
3015
+ {
3016
+ "ratio": "tall",
3017
+ "width": 1080,
3018
+ "height": 1920,
3019
+ "duration": 7,
3020
+ "video": {
3021
+ "objectKey": "authored-motion/1.0.0/shape-bridge/previews/6ba7dd297b99fd1bae8020c23b1937fab3eee5d3c029e0f2e31972fabb9593cb.mp4",
3022
+ "sha256": "6ba7dd297b99fd1bae8020c23b1937fab3eee5d3c029e0f2e31972fabb9593cb",
3023
+ "bytes": 605963,
3024
+ "contentType": "video/mp4"
3025
+ },
3026
+ "poster": {
3027
+ "objectKey": "authored-motion/1.0.0/shape-bridge/previews/6292c5b0d758e180e4ff02f70acb923ccf1ea0a243a8febe474e7a1fa383ec74.jpg",
3028
+ "sha256": "6292c5b0d758e180e4ff02f70acb923ccf1ea0a243a8febe474e7a1fa383ec74",
3029
+ "bytes": 44151,
3030
+ "contentType": "image/jpeg"
3031
+ }
3032
+ }
3033
+ ],
3034
+ "qa": {
3035
+ "status": "source-and-preview-hashes-verified",
3036
+ "previewCount": 3,
3037
+ "artisticApproval": "not-certified",
3038
+ "audioListened": false,
3039
+ "transferCertified": false
3040
+ },
3041
+ "restrictions": [
3042
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
3043
+ "Scene-specific data/count/layout limits are documented in the selected card.",
3044
+ "Opaque output only; no alpha export.",
3045
+ "Voice/music requires timeScale=1 until explicitly retimed.",
3046
+ "Inspect frame boundaries, typography and final audio before delivery."
3047
+ ]
3048
+ },
3049
+ {
3050
+ "id": "listing-mosaic",
3051
+ "version": "1.0.0",
3052
+ "title": "Visita en 10 segundos",
3053
+ "mechanism": "media-mosaic-anchored-pins",
3054
+ "intent": "Escena dirigida por fotos reales: mosaico, h\xE9roe y anotaciones ancladas a la imagen.",
3055
+ "niche": "inmobiliaria",
3056
+ "style": "foto",
3057
+ "duration": 12,
3058
+ "runtime": "canvas",
3059
+ "supportedRatios": [
3060
+ "16:9",
3061
+ "1:1",
3062
+ "9:16"
3063
+ ],
3064
+ "sourceSha256": "7c37e3bcac6b85b108c7e859f7083125b2e785fd046f217bf3b2158468e261b1",
3065
+ "cardSha256": "4982aeb9975e7852d315c6bb5e5d57626885d49634445c50d2df61dc10e88f30",
3066
+ "card": '# listing-mosaic \xB7 Visita en 10 segundos\n\n**Mecanismo.** Cuatro fotos caen como impresiones sobre una mesa, encajan en un mosaico y la foto h\xE9roe se abre a pantalla completa mientras las dem\xE1s se apartan. Sobre la h\xE9roe aparecen pines anclados a coordenadas de la imagen, que siguen pegados al objeto (ventana, piso, cl\xF3set) durante el recorte y el push-in lento. Cierra con una ficha de listado (precio, datos, CTA) que sube desde abajo.\n\n**\xDAsala para.** Inmobiliaria: departamento en venta con 3 atributos se\xF1alados en la foto. Hotel/renta corta: habitaci\xF3n con amenidades. Cocinas o muebles a medida: materiales y acabados. Restaurante: platillo nuevo con precio y CTA. **No la uses para.** Clientes sin fotos propias de calidad (la escena vive de la media); m\xE1s de 4 fotos o m\xE1s de ~4 atributos; explicar funcionamiento o procesos.\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| toss | 0.1 | 4 impresiones con borde blanco caen sobre la mesa, escalonadas 0.14 s, con giro y sombra. |\n| grid | 1.7 | Encajan en el mosaico (0.9 s); pierden borde y rotaci\xF3n. |\n| hero | 3.2 | La h\xE9roe se abre a pantalla completa (1.1 s); las otras se apartan y se desvanecen; empieza push-in 1\u21921.08 hasta `end`; velo degradado hacia el lado de la ficha y `note` (4.0\u20134.4). |\n| pins | 4.4 | Un pin cada 1.0 s: punto, anillo pulsante y chip con etiqueta (solo con la h\xE9roe ya completa). |\n| card | 8.0 | La ficha sube desde abajo (0.7 s): kicker, precio, datos, bot\xF3n CTA. |\n| end | 12 | Fin del push-in. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| `theme` / `brand` | kit (`estate`) y overrides | \u2014 |\n| `duration` `fps` `timeScale` | 12 s, 30 fps, 1 | si alargas, mueve `end` (push-in) |\n| `media.hero/a/b/c` | rutas a las 4 fotos (relativas a scene.html) | claves fijas; si falta una su hueco queda vac\xEDo; sin `hero` no hay pines |\n| `focus.<clave>` | punto focal [x, y] 0\u20131 del recorte tipo cover | \u2014 |\n| `pins[]` | `{at: [x, y] en coords 0\u20131 de la foto hero, label}` | pin fuera del recorte visible se omite y deja un warning en `getDiagnostics()` (hero vertical 1080\xD71920 en 16:9: solo se ve y \u2248 0.45\u20130.75); 3 con beats por defecto; label sin wrap (~30 car.) |\n| `copy.kicker` | may\xFAsculas mono en la ficha | maxW = ancho de ficha \u2212 80u |\n| `copy.price` | precio (display 96u) | texto libre, no se calcula |\n| `copy.facts` | l\xEDnea de datos | 1 l\xEDnea |\n| `copy.cta` | bot\xF3n `accent` | 1 l\xEDnea corta |\n| `copy.note` | aviso arriba-derecha (18u) | \u2014 |\n| `beats` / `sound.cues` | ver tabla y Sonido | \u2014 |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- Las 4 fotos (mismas claves), `focus`, `pins` (coordenadas sobre la foto hero), todo `copy`.\n- Voz: `hero` al empezar a describir, `pins` al primer atributo (cada pin +1.0 s, cadencia fija), `card` al precio.\n- Roles usados: `bg`+`line` (mesa), `surface` (chips, ficha), `ink` (etiquetas, precio, datos, nota), `muted` (kicker), `accent` (centro del pin, bot\xF3n). Blanco fijo para bordes de impresi\xF3n y anillos. Fuentes: `text`, `display` (precio), `mono` (kicker).\n\n## Qu\xE9 requiere tocar c\xF3digo\n- M\xE1s o menos de 4 fotos (`KEYS`, mosaico y lanzamientos est\xE1n hechos para 4).\n- Cadencia de pines, chips de 2 l\xEDneas, alto de ficha (330u fijo) o campos extra.\n- Video en lugar de fotos.\n\n## Formatos\n- 16:9 \u2014 h\xE9roe 58 % a la izquierda, `a`/`b` arriba-derecha, `c` abajo-derecha; ficha 560u abajo-derecha.\n- 1:1 \u2014 h\xE9roe 60 % a la izquierda, `a`/`b`/`c` apiladas a la derecha; ficha 520u abajo-derecha.\n- 9:16 \u2014 h\xE9roe arriba (60 % del alto), `a` abajo-izquierda, `b`/`c` apiladas abajo-derecha; ficha a todo el ancho abajo.\n\n## Sonido\n2 `paper` en `toss` (+0.3), `whoosh-short` en `grid` + `snap` +0.75 (encaje), `whoosh` en `hero` (apertura), 3 `pop` en `pins` +0/1/2 (uno por pin), `stamp` en `card`+0.5. Funciona muda.\n\n## Limitaciones\n- Un pin fuera del recorte no se dibuja (solo queda el warning `pin outside crop in <ratio>`); como el push-in sigue, uno cerca del borde puede salirse a media toma. Revisar diagn\xF3sticos y stills en los 3 formatos.\n- Velo: degradado negro 4 %\u219222 % hacia la derecha en 16:9 y hacia abajo (desde 0.4H) en 1:1/9:16; fotos ya oscuras en ese lado pierden detalle.\n- La ficha puede tapar pines de la zona inferior (derecha en 16:9/1:1).\n- Chips sin wrap: si el pin est\xE1 en x > 0.6W el chip crece a la izquierda y una etiqueta larga puede cortarse.\n- Precio y datos son texto libre; propiedad ficticia.\n\n## Adaptar a otro tema\n- Hotel boutique (`theme: estate`): 4 fotos de la suite en `media`, `pins` ["Terraza privada", "Tina de piedra", "Vista al mar"] con sus coordenadas, `copy.kicker` "Suite \xB7 Tulum", `copy.price` "$3,800 / noche", `copy.facts` "2 hu\xE9spedes \xB7 desayuno incluido", `copy.cta` "Reserva directo".\n- Cocinas a medida (`theme: cafe`): fotos de cocina, `pins` en cubierta/herrajes/luz, `copy.price` "Desde $180 mil", `copy.facts` "Dise\xF1o + instalaci\xF3n en 4 semanas", `copy.cta` "Cotiza tu cocina".\n- Restaurante (`theme: trattoria`): hero del platillo, `pins` en ingredientes, `copy.kicker` "Nuevo en el men\xFA", `copy.price` "$245", `copy.cta` "Pide en la app".\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Protagonista con anotaciones ancladas \u2190 ik_builds. Media: `media/sala.jpg` (hero, 1080\xD71920), `luz.jpg` (1080\xD71920), `madera.jpg` (900\xD71000, recortado para excluir una mano) y `detalle.jpg` (900\xD7560, recorte de `sala`) son frames extra\xEDdos de `demos/ugc-architecture-v4/generation/source.mp4` (sha256 `c341d454532534f5f06dc3e739071d00ee3394b0ab7abce9d1991d3acc40eeb7`): video generado con IA de un departamento ficticio, hecho para una demo interna de EditorBrain. No es una propiedad real; mantener `copy.note`. Registro completo en `PROVENANCE.md` (ra\xEDz de la biblioteca).\n',
3067
+ "bundle": {
3068
+ "objectKey": "authored-motion/1.0.0/listing-mosaic/33813605f6dbc01b3140af074ac3baea4440c55b41f0a35504e2fecb9208ffb4.tar.gz",
3069
+ "sha256": "33813605f6dbc01b3140af074ac3baea4440c55b41f0a35504e2fecb9208ffb4",
3070
+ "bytes": 3188724,
3071
+ "fileCount": 62,
3072
+ "contentType": "application/gzip"
3073
+ },
3074
+ "previews": [
3075
+ {
3076
+ "ratio": "wide",
3077
+ "width": 1920,
3078
+ "height": 1080,
3079
+ "duration": 12,
3080
+ "video": {
3081
+ "objectKey": "authored-motion/1.0.0/listing-mosaic/previews/e6d8ab64ee06d4547c82681011a87c9bbc17239043ac1db916630ac3c943465f.mp4",
3082
+ "sha256": "e6d8ab64ee06d4547c82681011a87c9bbc17239043ac1db916630ac3c943465f",
3083
+ "bytes": 4858118,
3084
+ "contentType": "video/mp4"
3085
+ },
3086
+ "poster": {
3087
+ "objectKey": "authored-motion/1.0.0/listing-mosaic/previews/1852c0aeac3d64b45945c4d5a8fabff3008326420469ccb2fd3e0a9ab0184062.jpg",
3088
+ "sha256": "1852c0aeac3d64b45945c4d5a8fabff3008326420469ccb2fd3e0a9ab0184062",
3089
+ "bytes": 181674,
3090
+ "contentType": "image/jpeg"
3091
+ }
3092
+ },
3093
+ {
3094
+ "ratio": "square",
3095
+ "width": 1080,
3096
+ "height": 1080,
3097
+ "duration": 12,
3098
+ "video": {
3099
+ "objectKey": "authored-motion/1.0.0/listing-mosaic/previews/4ccd023cb6bf7cbc53ea081892ec6465f952f025bc3b7feed67f0c598a9d26e3.mp4",
3100
+ "sha256": "4ccd023cb6bf7cbc53ea081892ec6465f952f025bc3b7feed67f0c598a9d26e3",
3101
+ "bytes": 3676424,
3102
+ "contentType": "video/mp4"
3103
+ },
3104
+ "poster": {
3105
+ "objectKey": "authored-motion/1.0.0/listing-mosaic/previews/3729b2848345bb08148943fd746563592da401ebe9f0bc2764de0b791861fd29.jpg",
3106
+ "sha256": "3729b2848345bb08148943fd746563592da401ebe9f0bc2764de0b791861fd29",
3107
+ "bytes": 135246,
3108
+ "contentType": "image/jpeg"
3109
+ }
3110
+ },
3111
+ {
3112
+ "ratio": "tall",
3113
+ "width": 1080,
3114
+ "height": 1920,
3115
+ "duration": 12,
3116
+ "video": {
3117
+ "objectKey": "authored-motion/1.0.0/listing-mosaic/previews/ee5b95a8d401d9e1b5520225df14d9c505156d85d14c12ac8daa463290c658c3.mp4",
3118
+ "sha256": "ee5b95a8d401d9e1b5520225df14d9c505156d85d14c12ac8daa463290c658c3",
3119
+ "bytes": 5130866,
3120
+ "contentType": "video/mp4"
3121
+ },
3122
+ "poster": {
3123
+ "objectKey": "authored-motion/1.0.0/listing-mosaic/previews/8dac7332eb9014300abc94eb7f7b514a14c5406b87a87329cd197d3b18264bfd.jpg",
3124
+ "sha256": "8dac7332eb9014300abc94eb7f7b514a14c5406b87a87329cd197d3b18264bfd",
3125
+ "bytes": 176678,
3126
+ "contentType": "image/jpeg"
3127
+ }
3128
+ }
3129
+ ],
3130
+ "qa": {
3131
+ "status": "source-and-preview-hashes-verified",
3132
+ "previewCount": 3,
3133
+ "artisticApproval": "not-certified",
3134
+ "audioListened": false,
3135
+ "transferCertified": false
3136
+ },
3137
+ "restrictions": [
3138
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
3139
+ "Scene-specific data/count/layout limits are documented in the selected card.",
3140
+ "Opaque output only; no alpha export.",
3141
+ "Voice/music requires timeScale=1 until explicitly retimed.",
3142
+ "Inspect frame boundaries, typography and final audio before delivery."
3143
+ ]
3144
+ },
3145
+ {
3146
+ "id": "phone-pushin",
3147
+ "version": "1.0.0",
3148
+ "title": "Tres toques",
3149
+ "mechanism": "device-camera-pushin",
3150
+ "intent": "Demo de app m\xF3vil con c\xE1mara que empuja a cada toque; el resultado sale como objeto.",
3151
+ "niche": "fitness",
3152
+ "style": "UI",
3153
+ "duration": 12,
3154
+ "runtime": "canvas",
3155
+ "supportedRatios": [
3156
+ "16:9",
3157
+ "1:1",
3158
+ "9:16"
3159
+ ],
3160
+ "sourceSha256": "2e32ef7d715b128a4582a7c40ea24c28b060ef38274d68bb2ed71a60bffaf0ac",
3161
+ "cardSha256": "3fabf7079da366e152773b09e5d183b8c31c744a7e409eacf0a96b5d268fa70f",
3162
+ "card": '# phone-pushin \xB7 Tres toques\n\n**Mecanismo.** Un tel\xE9fono dibujado recorre un flujo de app de tres pantallas (lista \u2192 elegir lugar \u2192 confirmaci\xF3n) dirigido como un camar\xF3grafo: antes de cada toque la c\xE1mara empuja hacia el objetivo, sostiene durante el toque y se aleja cuando la pantalla ya cambi\xF3. Un contador fuera de c\xE1mara suma los toques y el resultado (un boleto) sale del tel\xE9fono como objeto f\xEDsico.\n\n**\xDAsala para.** Gimnasio/estudio: "reserva clase y lugar en 3 toques". Cine/teatro: comprar boleto con asiento. Coworking: reservar escritorio. Barber\xEDa o cl\xEDnica: apartar turno numerado. **No la uses para.** Flujos con m\xE1s o menos de 3 pasos o sin selecci\xF3n en cuadr\xEDcula; mostrar la app real del cliente (esta UI es gen\xE9rica e ilustrativa: usa grabaci\xF3n de pantalla); productos web/escritorio.\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| phone | 0.1 | El tel\xE9fono sube 120u y aparece (0.8 s) con `app` y las tarjetas de `classes`. |\n| tap1 | 2.0 | Push-in desde 1.25 a la tarjeta `pick`; toque (sombra + ripple `accent2`), la tarjeta se ti\xF1e; la pantalla desliza al detalle (2.25\u20132.7); el contador aparece (1.8) y pasa a 1. |\n| tap2 | 4.4 | Push-in al lugar `spot` (bajo el r\xF3tulo `chooseSpot`); el lugar rebota en `accent`; el bot\xF3n se activa y muestra el n\xFAmero (4.5\u20134.8). |\n| tap3 | 6.6 | Push-in al bot\xF3n; toque; pantalla de confirmaci\xF3n (6.85\u20137.3): c\xEDrculo `accent`, palomita y `done`. |\n| ticket | 8.0 | El bloque de la pantalla se desvanece y el boleto `accent2` vuela fuera del tel\xE9fono con rebote (0.9 s); `closing` entra 8.9\u20139.5 (no en 9:16). |\n| end | 12 | No lo usa el draw; estado final ~2.5 s. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| `theme` / `brand` | kit (`sport`) y overrides | cualquier kit: texto y marcas sobre `accent`/`accent2` eligen negro o blanco por contraste |\n| `duration` `fps` `timeScale` | 12 s, 30 fps, 1 | \u2014 |\n| `copy.app` | encabezado de la lista | ~16 car. |\n| `copy.classes[]` | `[nombre, hora, persona, estado]` por tarjeta | m\xE1x. 4 (la 5\xAA sale cortada); estado igual a `fullLabel` = tarjeta apagada (`muted`), otro estado en `accent2`; sin maxW |\n| `copy.pick` | \xEDndice 0-based de la tarjeta tocada | 0..n\u22121 |\n| `copy.spot` | lugar elegido | 1\u201312 (cuadr\xEDcula fija 3\xD74) |\n| `copy.taken[]` | lugares ocupados | no se valida que `spot` no est\xE9 ocupado |\n| `copy.button` | bot\xF3n con `{n}` = spot ("\u2014" antes de elegir) | ~18 car. |\n| `copy.ticketTitle` | t\xEDtulo del boleto con `{n}` | \u2264 ~8 car. (heavy 110 a escala; el c\xF3digo ocupa el lado derecho) |\n| `copy.ticketSub` | subt\xEDtulo del boleto | texto libre: no se arma de `classes[pick]`, mantenerlo consistente a mano |\n| `copy.counter` / `counterPlural` | etiqueta del contador (singular/plural) | `{n}` se elimina; el n\xFAmero va aparte en grande; se muestra en may\xFAsculas |\n| `copy.closing` | frase final (heavy 64/46u) | solo 16:9 y 1:1; wrap al ancho libre a la izquierda del tel\xE9fono (\u2248767 px en 16:9, \u2248370 px en 1:1: el default ocupa 2 l\xEDneas); 2\u20133 l\xEDneas m\xE1x. |\n| `copy.chooseSpot` | r\xF3tulo mono sobre la cuadr\xEDcula (en may\xFAsculas) | ~20 car. |\n| `copy.done` | texto de la pantalla de confirmaci\xF3n (display 64) | ~10 car. |\n| `copy.fullLabel` | estado que marca una clase como llena | coincidencia exacta con `classes[i][3]` |\n| `copy.illustrative` | aviso abajo-derecha, siempre visible | \u2014 |\n| `beats` / `sound.cues` | ver tabla y Sonido | \u2014 |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- Todo `copy`, incluidas clases, estados, `pick`, `spot`, `taken`, `chooseSpot`, `done`, `fullLabel`.\n- Voz: `tap1`\u2013`tap3` en cada verbo de acci\xF3n (la c\xE1mara se anticipa 0.75 s), `ticket` en el resultado.\n- Roles usados: `surface` (pantalla), `bg` (tarjetas, lugares libres), `ink`, `muted`, `accent` (selecci\xF3n, lugar, bot\xF3n activo, palomita, n\xFAmero del contador), `accent2` (estado disponible, ripple, boleto). Fuentes: `heavy`, `display`, `text`, `mono`.\n\n## Qu\xE9 requiere tocar c\xF3digo\n- N\xFAmero de toques/pantallas (3 fijo); el tercer toque siempre va al bot\xF3n.\n- M\xE1s de 12 lugares o de 4 tarjetas; `closing` en 9:16.\n- Cuerpo del tel\xE9fono (negro fijo) y c\xF3digo del boleto (patr\xF3n fijo).\n\n## Formatos\n- 16:9 \u2014 tel\xE9fono 430u en 0.6W; contador 230u a la izquierda (0.62H) con `closing` debajo; el boleto vuela arriba-izquierda (0.16H); zoom de push 1.75\xD7.\n- 1:1 \u2014 tel\xE9fono 380u en 0.62W; contador 180u a la izquierda; boleto arriba-izquierda (0.1H); zoom 1.65\xD7.\n- 9:16 \u2014 tel\xE9fono 560u centrado (0.53H); contador 160u arriba-izquierda; el boleto aterriza abajo centrado sobre el tel\xE9fono; sin `closing`; zoom 1.55\xD7.\n\n## Sonido\n`click` en cada tap; `whoosh-short` en `tap1`+0.3 (cambio de pantalla); `pop` en `tap2`+0.05 (lugar); `success` en `tap3`+0.4 (confirmaci\xF3n); `whoosh` en `ticket` + `paper` +0.5 (sale el boleto). Funciona muda.\n\n## Limitaciones\n- UI y datos ilustrativos; conservar `illustrative`.\n- `ticketSub` duplica a mano los datos de `classes[pick]`.\n- Textos de tarjeta y bot\xF3n sin maxW: nombres largos se salen de la pantalla del tel\xE9fono.\n- El contador aparece en "0" 0.2 s antes del primer toque.\n\n## Adaptar a otro tema\n- Cine (`theme: night`): `copy.app` "Cartelera \xB7 Viernes", `classes` con funciones de una pel\xEDcula ficticia, `button` "Comprar asiento {n}", `ticketTitle` "Fila {n}", `ticketSub` "La \xFAltima \xF3rbita \xB7 19:30 \xB7 Sala 4", `closing` "Tu boleto en 3 toques.".\n- Coworking (`theme: transit`): `classes` [["Sala abierta","09:00","Piso 2","8 lugares"], \u2026], `chooseSpot` "Elige tu mesa", `button` "Reservar mesa {n}", `ticketTitle` "Mesa {n}", `ticketSub` "Sala abierta \xB7 09:00 \xB7 Piso 2", `closing` "Listo en 3 toques.".\n- Estudio de yoga (`theme: sage`): `classes` con Vinyasa/Yin/Power, `fullLabel` "Sin cupo" (y ese estado en la clase llena), `done` "\xA1Nos vemos!", `ticketSub` igual a la clase elegida, beats `tap*` a la voz.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Objeto persistente (el tel\xE9fono) con contenido y contenedor en tiempos separados \u2190 twoclipping (ver PROVENANCE.md de la biblioteca). Sin media externa.\n',
3163
+ "bundle": {
3164
+ "objectKey": "authored-motion/1.0.0/phone-pushin/7924801e5cea4e2bea3a45854a611f740209411ebd7b8f841475d8354b9e1be2.tar.gz",
3165
+ "sha256": "7924801e5cea4e2bea3a45854a611f740209411ebd7b8f841475d8354b9e1be2",
3166
+ "bytes": 2936041,
3167
+ "fileCount": 58,
3168
+ "contentType": "application/gzip"
3169
+ },
3170
+ "previews": [
3171
+ {
3172
+ "ratio": "wide",
3173
+ "width": 1920,
3174
+ "height": 1080,
3175
+ "duration": 12,
3176
+ "video": {
3177
+ "objectKey": "authored-motion/1.0.0/phone-pushin/previews/dd79dbae9f23b83e98e602aa45e58966d6340ac4542ea066addee596457b5d39.mp4",
3178
+ "sha256": "dd79dbae9f23b83e98e602aa45e58966d6340ac4542ea066addee596457b5d39",
3179
+ "bytes": 1465443,
3180
+ "contentType": "video/mp4"
3181
+ },
3182
+ "poster": {
3183
+ "objectKey": "authored-motion/1.0.0/phone-pushin/previews/ebdb98c2f27b23cfb6bb58189f43188d86680a4dedc47154495d08099dd16a7e.jpg",
3184
+ "sha256": "ebdb98c2f27b23cfb6bb58189f43188d86680a4dedc47154495d08099dd16a7e",
3185
+ "bytes": 85640,
3186
+ "contentType": "image/jpeg"
3187
+ }
3188
+ },
3189
+ {
3190
+ "ratio": "square",
3191
+ "width": 1080,
3192
+ "height": 1080,
3193
+ "duration": 12,
3194
+ "video": {
3195
+ "objectKey": "authored-motion/1.0.0/phone-pushin/previews/12a4bd34ccacd834d5e2a1976723992b98c930e8aced4f02704386c99b9a1a5c.mp4",
3196
+ "sha256": "12a4bd34ccacd834d5e2a1976723992b98c930e8aced4f02704386c99b9a1a5c",
3197
+ "bytes": 1328408,
3198
+ "contentType": "video/mp4"
3199
+ },
3200
+ "poster": {
3201
+ "objectKey": "authored-motion/1.0.0/phone-pushin/previews/3f70e0a04b087e52a27ddced7a288e09ab22ab396210e973197923595611eb85.jpg",
3202
+ "sha256": "3f70e0a04b087e52a27ddced7a288e09ab22ab396210e973197923595611eb85",
3203
+ "bytes": 65009,
3204
+ "contentType": "image/jpeg"
3205
+ }
3206
+ },
3207
+ {
3208
+ "ratio": "tall",
3209
+ "width": 1080,
3210
+ "height": 1920,
3211
+ "duration": 12,
3212
+ "video": {
3213
+ "objectKey": "authored-motion/1.0.0/phone-pushin/previews/0d609c634b18eef3df3581b04e0d3d0342bda4a4b89d704722c5ac2e69f28313.mp4",
3214
+ "sha256": "0d609c634b18eef3df3581b04e0d3d0342bda4a4b89d704722c5ac2e69f28313",
3215
+ "bytes": 1725586,
3216
+ "contentType": "video/mp4"
3217
+ },
3218
+ "poster": {
3219
+ "objectKey": "authored-motion/1.0.0/phone-pushin/previews/f8a766e2828f9cada77e754269e07767b4875f554f529a04f764af0e27a75c1b.jpg",
3220
+ "sha256": "f8a766e2828f9cada77e754269e07767b4875f554f529a04f764af0e27a75c1b",
3221
+ "bytes": 77585,
3222
+ "contentType": "image/jpeg"
3223
+ }
3224
+ }
3225
+ ],
3226
+ "qa": {
3227
+ "status": "source-and-preview-hashes-verified",
3228
+ "previewCount": 3,
3229
+ "artisticApproval": "not-certified",
3230
+ "audioListened": false,
3231
+ "transferCertified": false
3232
+ },
3233
+ "restrictions": [
3234
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
3235
+ "Scene-specific data/count/layout limits are documented in the selected card.",
3236
+ "Opaque output only; no alpha export.",
3237
+ "Voice/music requires timeScale=1 until explicitly retimed.",
3238
+ "Inspect frame boundaries, typography and final audio before delivery."
3239
+ ]
3240
+ },
3241
+ {
3242
+ "id": "pricing-slider",
3243
+ "version": "1.0.0",
3244
+ "title": "Un control, todos los precios",
3245
+ "mechanism": "coupled-control",
3246
+ "intent": "Comparar opciones cuyo ganador depende de una variable; puntos de equilibrio marcados.",
3247
+ "niche": "SaaS",
3248
+ "style": "UI",
3249
+ "duration": 12,
3250
+ "runtime": "canvas",
3251
+ "supportedRatios": [
3252
+ "16:9",
3253
+ "1:1",
3254
+ "9:16"
3255
+ ],
3256
+ "sourceSha256": "af380a5ff206ba58d08957bbb933a7641d85cefd5eca62c151a69c1f76e7930d",
3257
+ "cardSha256": "51603e555eeff66df8a703a659e12c66799699bdd2fd4d50f0303728beea4136",
3258
+ "card": '# pricing-slider \xB7 Un control, todos los precios\n\n**Mecanismo.** Un cursor arrastra un solo slider (el volumen del cliente) y cada tarjeta recalcula su precio `base + per\xB7n` con ese mismo valor. La insignia "m\xE1s barato" salta en arco de una tarjeta a otra cada vez que el valor cruza un punto de equilibrio; esos puntos se calculan entre pares de planes y se marcan a mano sobre el riel cuando se cruzan. Cierra con la respuesta: depende del volumen, no del nombre del plan.\n\n**\xDAsala para.** SaaS con planes por uso: "\xBFqu\xE9 plan te conviene seg\xFAn tus clientes al mes?". Coworking: pase diario vs mensual vs dedicado seg\xFAn d\xEDas al mes. Gimnasio: clase suelta vs paquete vs membres\xEDa seg\xFAn visitas. Telefon\xEDa/datos: pago por GB vs ilimitado. **No la uses para.** Precios por tramos, m\xEDnimos o descuentos escalonados (el modelo es lineal); m\xE1s de 3 opciones; comparar funciones o calidad.\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| cards | 0.1 | Pregunta y tarjetas entran (desfase 0.1 s, rebote); insignia sobre el m\xE1s barato; el cursor llega 0.5\u20131.0 y agarra el pomo a 0.95. |\n| answer | 9.4 | Sale la pregunta (9.1\u20139.4) y entra la respuesta (0.5 s); el cursor suelta y se va (9.7\u201310.3). |\n| end | 12 | No lo usa el draw. |\nEntre `cards` y `answer` manda `path` (s, valor): 3\u219214 en 1.0\u20133.0 (cruza 9.8 a 2.09 s: B\xE1sico\u2192Pro), 14\u219264 en 3.8\u20136.4 (cruza 50 a 5.33 s: Pro\u2192Escala), 64\u219230 en 7.3\u20139.0 (cruza 50 a 8.10 s: Escala\u2192Pro), queda en 30. En cada cruce la insignia viaja 0.45 s, la tarjeta ganadora se eleva con borde `accent` y aparece "aqu\xED cambia \xB7 N".\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| `theme` / `brand` | kit (`indigo`) y overrides | \u2014 |\n| `duration` `fps` `timeScale` | 12 s, 30 fps, 1 | `duration` tambi\xE9n limita el muestreo de cruces |\n| `currency` | prefijo del precio | se concatena tal cual; sin separador de miles |\n| `unit` | etiqueta bajo el pomo: "N unit" | 1 l\xEDnea |\n| `max` | tope del riel | equilibrios fuera de (0, max) no se marcan |\n| `plans[]` | `{name, base, per, formula}` | m\xE1x. 3 en los 3 formatos (4 desbordan); precio calculado y redondeado; empate = gana el primero; `formula` es texto libre que no se valida contra `base`/`per`; en 1:1 `formula` \u2264 ~22 car. (228 px) |\n| `path` | keyframes `[segundo, valor]` con easing inOutCubic | tiempos en segundos de escena, no beats; si no cruza un equilibrio, su marca queda sin etiqueta |\n| `copy.question` | pregunta (display, sin wrap) | \u2264 ~30 car. |\n| `copy.answer` | respuesta, wrap a W\u2212140u | 2 l\xEDneas |\n| `copy.badge` | texto sobre `accent` (blanco o `ink`, el de m\xE1s contraste) | ~20 car. |\n| `copy.perMonth` | sufijo bajo el precio | corto |\n| `copy.switch` | etiqueta a mano "switch \xB7 N" en cada equilibrio | N con 1 decimal y punto |\n| `beats` / `sound.cues` | ver tabla y Sonido | \u2014 |\nCalculado: precios, plan ganador, instantes de cruce (muestreo de `path` a 120 Hz) y puntos de equilibrio `(baseB \u2212 baseA)/(perA \u2212 perB)`.\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- `plans`, `currency`, `unit`, `max`, todo `copy`.\n- Voz: retimea `path` (cu\xE1ndo arrastra y a qu\xE9 valor) y `answer`.\n- Roles usados: `bg`, `surface` (tarjetas, pomo), `ink` (texto, marcas, cursor), `muted` (f\xF3rmula, sufijo), `line` (riel), `accent` (precio ganador, insignia, relleno del riel, borde, etiquetas de cruce). `accent2` no se usa. Fuentes: `display` (pregunta, respuesta, precios), `heavy` (nombres), `text`, `hand` (cruces).\n\n## Qu\xE9 requiere tocar c\xF3digo\n- M\xE1s de 3 planes; precios no lineales (tramos, m\xEDnimos, topes).\n- Formato num\xE9rico (miles, decimales).\n- Sonido autom\xE1tico en cada cruce: la escena no define `cues(S)` (el runtime lo admite, como en zoom-niche y price-anatomy); `S.switches` ya tiene los instantes.\n\n## Formatos\n- 16:9 \u2014 3 tarjetas de 520u en fila (y 270u); pregunta centrada arriba; riel de 0.6W centrado abajo (H\u2212210u).\n- 1:1 \u2014 3 tarjetas de 300u en fila; riel de 0.76W; textos de tarjeta m\xE1s chicos.\n- 9:16 \u2014 tarjetas apiladas a todo el ancho (280u de alto), precio alineado a la derecha; pregunta alineada a la izquierda; la insignia queda en el borde derecho y viaja en vertical; riel abajo (H\u2212330u).\n\n## Sonido\n`click` a 1.0 s (el cursor agarra; est\xE1 en segundos, no en beat) y `chime` en `answer` (resoluci\xF3n). Los cruces no suenan: a\xF1ade cues en segundos absolutos (\u22482.09, 5.33, 8.10 con el `path` por defecto) si los quieres marcar. Funciona muda.\n\n## Limitaciones\n- Planes y precios ilustrativos; la `formula` mostrada puede contradecir `base`/`per` si no se actualiza a mano.\n- Pregunta sin wrap (overflow solo como warning).\n- Al cambiar `path` hay que recalcular a mano los segundos de cualquier cue nueva.\n\n## Adaptar a otro tema\n- Coworking (`theme: transit`): `unit` "d\xEDas al mes", `max` 30, `plans` [{D\xEDa, 0, 250, "$250 por d\xEDa"}, {Flex, 1500, 120, "$1,500 + $120 por d\xEDa"}, {Fijo, 4200, 0, "$4,200 fijo"}] (equilibrios calculados \u224811.5 y 22.5), `path` [[0,4],[1,4],[3,15],[3.8,15],[6.4,28],[7.3,28],[9,18],[12,18]].\n- Gimnasio (`theme: sport`): `unit` "visitas al mes", `max` 20, `plans` [{Clase suelta, 0, 120, \u2026}, {Paquete, 400, 50, \u2026}, {Ilimitado, 1100, 0, \u2026}] (equilibrios \u22485.7 y 14), `path` 2\u21928\u219218\u219212, `copy.question` "\xBFQu\xE9 te sale m\xE1s barato?".\n- SaaS de env\xEDos (`theme: indigo`): `unit` "env\xEDos al mes", `currency` "US$", `plans` con `per` decimal (p.ej. 0.4; el precio mostrado se redondea a entero), `copy.switch` "cambia".\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Mecanismo original. Sin media externa.\n',
3259
+ "bundle": {
3260
+ "objectKey": "authored-motion/1.0.0/pricing-slider/a065140206e55e31a46e29e842081cf1eb92702620bd5842808dd115a09f6ddb.tar.gz",
3261
+ "sha256": "a065140206e55e31a46e29e842081cf1eb92702620bd5842808dd115a09f6ddb",
3262
+ "bytes": 2934887,
3263
+ "fileCount": 58,
3264
+ "contentType": "application/gzip"
3265
+ },
3266
+ "previews": [
3267
+ {
3268
+ "ratio": "wide",
3269
+ "width": 1920,
3270
+ "height": 1080,
3271
+ "duration": 12,
3272
+ "video": {
3273
+ "objectKey": "authored-motion/1.0.0/pricing-slider/previews/60ace0b50e02270e44bc8d6256b291061e28e9a6da4c557fc5bc831f25e01bd8.mp4",
3274
+ "sha256": "60ace0b50e02270e44bc8d6256b291061e28e9a6da4c557fc5bc831f25e01bd8",
3275
+ "bytes": 953264,
3276
+ "contentType": "video/mp4"
3277
+ },
3278
+ "poster": {
3279
+ "objectKey": "authored-motion/1.0.0/pricing-slider/previews/5a7f899ced3666cdc07ec1d1a578236e8cfe345282cd7f4538947e5343c29761.jpg",
3280
+ "sha256": "5a7f899ced3666cdc07ec1d1a578236e8cfe345282cd7f4538947e5343c29761",
3281
+ "bytes": 108156,
3282
+ "contentType": "image/jpeg"
3283
+ }
3284
+ },
3285
+ {
3286
+ "ratio": "square",
3287
+ "width": 1080,
3288
+ "height": 1080,
3289
+ "duration": 12,
3290
+ "video": {
3291
+ "objectKey": "authored-motion/1.0.0/pricing-slider/previews/fa1f1035a61aa5115de6b29d973816e695e1f47d55c281aef0dd3ba61ec8ce06.mp4",
3292
+ "sha256": "fa1f1035a61aa5115de6b29d973816e695e1f47d55c281aef0dd3ba61ec8ce06",
3293
+ "bytes": 800997,
3294
+ "contentType": "video/mp4"
3295
+ },
3296
+ "poster": {
3297
+ "objectKey": "authored-motion/1.0.0/pricing-slider/previews/f06b55a6c0d0a14af767ee2bf47918c84e853c3ec4541dc04726118ef8e9cee4.jpg",
3298
+ "sha256": "f06b55a6c0d0a14af767ee2bf47918c84e853c3ec4541dc04726118ef8e9cee4",
3299
+ "bytes": 81144,
3300
+ "contentType": "image/jpeg"
3301
+ }
3302
+ },
3303
+ {
3304
+ "ratio": "tall",
3305
+ "width": 1080,
3306
+ "height": 1920,
3307
+ "duration": 12,
3308
+ "video": {
3309
+ "objectKey": "authored-motion/1.0.0/pricing-slider/previews/d70b047ba4567e5f118a7f7543ab1d4b9a869ed112297f647be0bb8e476d8e7d.mp4",
3310
+ "sha256": "d70b047ba4567e5f118a7f7543ab1d4b9a869ed112297f647be0bb8e476d8e7d",
3311
+ "bytes": 960641,
3312
+ "contentType": "video/mp4"
3313
+ },
3314
+ "poster": {
3315
+ "objectKey": "authored-motion/1.0.0/pricing-slider/previews/dde906576dc7cb6043662ecb51c1e7b14367d8133f00c117e3bea5361e4fead3.jpg",
3316
+ "sha256": "dde906576dc7cb6043662ecb51c1e7b14367d8133f00c117e3bea5361e4fead3",
3317
+ "bytes": 107372,
3318
+ "contentType": "image/jpeg"
3319
+ }
3320
+ }
3321
+ ],
3322
+ "qa": {
3323
+ "status": "source-and-preview-hashes-verified",
3324
+ "previewCount": 3,
3325
+ "artisticApproval": "not-certified",
3326
+ "audioListened": false,
3327
+ "transferCertified": false
3328
+ },
3329
+ "restrictions": [
3330
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
3331
+ "Scene-specific data/count/layout limits are documented in the selected card.",
3332
+ "Opaque output only; no alpha export.",
3333
+ "Voice/music requires timeScale=1 until explicitly retimed.",
3334
+ "Inspect frame boundaries, typography and final audio before delivery."
3335
+ ]
3336
+ },
3337
+ {
3338
+ "id": "page-critique",
3339
+ "version": "1.0.0",
3340
+ "title": "Cr\xEDtica de landing",
3341
+ "mechanism": "annotation-layer-fix",
3342
+ "intent": "Auditor\xEDa de una p\xE1gina o pantalla: calor, pliegue, problema marcado y arreglo f\xEDsico.",
3343
+ "niche": "marketing",
3344
+ "style": "UI",
3345
+ "duration": 13,
3346
+ "runtime": "canvas",
3347
+ "supportedRatios": [
3348
+ "16:9",
3349
+ "1:1",
3350
+ "9:16"
3351
+ ],
3352
+ "sourceSha256": "688131f212f4cdbcebf97397977c068c266cbfe12355f6464362a32eaf54b19c",
3353
+ "cardSha256": "48360c503eea6e946da7f0435f7aba8562544f6b735db8edd0aac11b2b4e220a",
3354
+ "card": '# page-critique \xB7 Cr\xEDtica de landing\n\n**Mecanismo.** Una landing esquem\xE1tica dentro de una ventana de navegador se desplaza como la leer\xEDa alguien (hasta el fondo y de vuelta). Encima se pinta una capa de cr\xEDtica: calor de atenci\xF3n que decae con la profundidad, el pliegue trazado a mano a un viewport de altura y una flecha hacia el bot\xF3n enterrado al final. Luego el arreglo ocurre f\xEDsicamente: el bot\xF3n viaja desde el fondo hasta la primera pantalla, se encierra a mano y la m\xE9trica rueda de `before` a `after`.\n\n**\xDAsala para.** Consultor CRO/agencia: "tu bot\xF3n est\xE1 bajo el pliegue". Auditor\xEDa de landing de negocio local (estudio de dise\xF1o, cl\xEDnica, despacho): "s\xFAbelo y agenda m\xE1s". Curso de marketing: explicar pliegue y atenci\xF3n. SaaS: registro enterrado al final de la home. **No la uses para.** Problemas que no son de posici\xF3n (copy, velocidad, precio): el arreglo siempre es subir el bot\xF3n; mostrar la p\xE1gina real del cliente (es un esquema dibujado, no una captura); apps m\xF3viles.\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| page | 0.1 | Aparece la ventana (0.7 s) con la landing arriba. |\n| scroll | 1.2 | Scroll hasta el fondo (hasta 3.2): secciones y el bot\xF3n enterrado al final. |\n| up | 3.4 | Vuelve arriba (0.9 s). |\n| heat | 4.4 | Manchas de calor `neg`\u2192`accent2` concentradas arriba (0.8 s). |\n| fold | 5.2 | L\xEDnea a mano `accent2` a un viewport de altura + texto `fold` (0.6 s). |\n| buried | 6.1 | Fuera de la ventana: flecha a mano hacia abajo + nota `buried`. |\n| lift | 7.3 | Salen calor y nota; el bot\xF3n sube en arco del fondo a debajo del subt\xEDtulo (0.9 s) y empuja la imagen; sale el pliegue (7.7\u20138.3); c\xEDrculo a mano + `fix` (8.2\u20138.7). |\n| metric | 8.7 | Entra `metricLabel`; `before` se tacha mientras el n\xFAmero rueda (9.1\u201310.1) y termina en `pos`. |\n| end | 13 | No lo usa el draw; estado final ~2.9 s. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| `theme` / `brand` | kit (`ledger`) y overrides | \u2014 |\n| `duration` `fps` `timeScale` | 13 s, 30 fps, 1 | \u2014 |\n| `page.brand` | marca en la esquina de la p\xE1gina | corto |\n| `page.url` | dominio en la barra del navegador (mono) | 1 l\xEDnea corta |\n| `page.nav[]` | men\xFA de la p\xE1gina, desde x 560 u cada 110 u | 3 entradas de ~10 car. (una 4\xAA toca el borde) |\n| `page.hero` | titular (wrap a 760 u de p\xE1gina) | 2 l\xEDneas recomendado; m\xE1s l\xEDneas empujan todo hacia abajo |\n| `page.sub` | subt\xEDtulo | 1 l\xEDnea |\n| `page.sections[]` | t\xEDtulos de secci\xF3n con barras de texto falso (360 u c/u) | \u22646: la p\xE1gina mide 3200 u fijos y el bot\xF3n vive en y=2960; con m\xE1s, el contenido pasa por debajo del bot\xF3n; con menos queda un hueco vac\xEDo |\n| `page.cta` | texto del bot\xF3n (`accent`) | ~22 car. |\n| `copy.fold` | nota a mano en el pliegue | 1 l\xEDnea; el "70 %" es texto, no se calcula |\n| `copy.buried` | nota junto a la flecha | wrap autom\xE1tico |\n| `copy.fix` | etiqueta a mano junto al bot\xF3n arreglado | 1\u20132 palabras |\n| `copy.metricLabel` | etiqueta de la m\xE9trica | \u2264 ~20 car. en 1:1 (\u2248317 px libres) |\n| `copy.before` / `after` | se leen con `parseFloat`; el valor rueda y se muestra con 1 decimal + `%` | punto decimal ("1,2%" se lee como 1); siempre porcentaje; `before` se muestra tal cual |\n| `copy.note` | aviso abajo-derecha | 18u |\n| `beats` / `sound.cues` | ver tabla y Sonido | \u2014 |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- `page.*` (marca, URL, men\xFA, titular, secciones, bot\xF3n), todo `copy`, cifras (ilustrativas).\n- Voz: `fold` y `buried` en el diagn\xF3stico, `lift` en "s\xFAbelo", `metric` en el resultado.\n- Roles usados: `surface` (ventana, p\xE1gina), `line` (barra del navegador, placeholders), `ink`, `muted`, `accent` (bot\xF3n, tinte de bloques), `accent2` (pliegue, flecha, notas, c\xEDrculo, calor), `neg` (n\xFAcleo del calor, sem\xE1foro), `pos` (sem\xE1foro, m\xE9trica final). Fuentes: `heavy` (marca), `display` (titular, secciones, m\xE9trica), `text`, `mono` (URL, `before`), `hand` (notas).\n\n## Qu\xE9 requiere tocar c\xF3digo\n- Mapa de calor: las 6 manchas tienen posiciones fijas en c\xF3digo (ilustrativo, no datos; no sigue al contenido si cambia el titular).\n- Otro arreglo que no sea mover el bot\xF3n; altura de p\xE1gina; vista m\xF3vil; m\xE9trica no porcentual.\n\n## Formatos\n- 16:9 \u2014 ventana a la izquierda (0.55W \xD7 0.82H); flecha, nota y m\xE9trica a la derecha de la ventana (m\xE9trica a 0.42H, 150u).\n- 1:1 \u2014 ventana 0.6W \xD7 0.78H; notas y m\xE9trica en la columna derecha estrecha (\u2248317 px, m\xE9trica 110u).\n- 9:16 \u2014 ventana 0.86W \xD7 0.6H desde 0.2H; flecha y nota debajo de la ventana; la m\xE9trica tambi\xE9n debajo, cuando la nota ya sali\xF3.\n\n## Sonido\n`whoosh` en `scroll`, `whoosh-short` en `up`, `swell` en `heat` (atenci\xF3n), `scribble` en `fold` y en `buried` (anotaciones), `whoosh` en `lift` + `snap` +0.85 (el bot\xF3n aterriza), `coin` en `metric`+0.4 (rueda la cifra). Funciona muda.\n\n## Limitaciones\n- P\xE1gina, calor y cifras ilustrativas (conservar `note`); el calor no se deriva de datos: no presentarlo como medido.\n- M\xE9trica: interpolaci\xF3n lineal, 1 decimal y `%` fijos.\n- Titulares de 3+ l\xEDneas o muchas secciones desalinean calor, pliegue y bot\xF3n.\n\n## Adaptar a otro tema\n- Cl\xEDnica dental (`theme: sage`): `page.brand` "Cl\xEDnica Sonr\xEDe", `page.hero` "Ortodoncia invisible sin citas eternas", `page.sections` (\u22646), `page.cta` "Agenda tu valoraci\xF3n", `copy.metricLabel` "citas agendadas", `copy.before` "0.8%", `copy.after` "2.1%", `page.url` "clinicasonrie.mx", `page.nav` ["Servicios","Precios","Citas"].\n- SaaS (`theme: indigo`): `page.brand` "Facturo", `page.url` "facturo.app", `page.hero` "Facturaci\xF3n autom\xE1tica para freelancers", `page.cta` "Prueba gratis", `copy.fix` "arriba", `copy.metricLabel` "registros por visita", `copy.before` "2.0%", `copy.after` "4.6%".\n- Escuela en l\xEDnea (`theme: night`): `page.hero` "Aprende Excel en 4 semanas", `page.cta` "Inscr\xEDbete", `copy.fold` "Casi nadie baja de aqu\xED", `copy.buried` "tu inscripci\xF3n est\xE1 al final".\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Capa de anotaciones sobre un protagonista \u2190 ik_builds. Sin media externa.\n',
3355
+ "bundle": {
3356
+ "objectKey": "authored-motion/1.0.0/page-critique/40e1c88e3a71265f362d4474d645139de562d9282b083f663d8819fb774514ad.tar.gz",
3357
+ "sha256": "40e1c88e3a71265f362d4474d645139de562d9282b083f663d8819fb774514ad",
3358
+ "bytes": 2935366,
3359
+ "fileCount": 58,
3360
+ "contentType": "application/gzip"
3361
+ },
3362
+ "previews": [
3363
+ {
3364
+ "ratio": "wide",
3365
+ "width": 1920,
3366
+ "height": 1080,
3367
+ "duration": 13,
3368
+ "video": {
3369
+ "objectKey": "authored-motion/1.0.0/page-critique/previews/ca6ece1bfb93b414c314c5eea9da25a85ccc0283a419813daca16bbce84d0984.mp4",
3370
+ "sha256": "ca6ece1bfb93b414c314c5eea9da25a85ccc0283a419813daca16bbce84d0984",
3371
+ "bytes": 1373353,
3372
+ "contentType": "video/mp4"
3373
+ },
3374
+ "poster": {
3375
+ "objectKey": "authored-motion/1.0.0/page-critique/previews/52772aa1fbcc3115a71309faa11c945c78252a9af74191f0bf26ad777845963e.jpg",
3376
+ "sha256": "52772aa1fbcc3115a71309faa11c945c78252a9af74191f0bf26ad777845963e",
3377
+ "bytes": 117262,
3378
+ "contentType": "image/jpeg"
3379
+ }
3380
+ },
3381
+ {
3382
+ "ratio": "square",
3383
+ "width": 1080,
3384
+ "height": 1080,
3385
+ "duration": 13,
3386
+ "video": {
3387
+ "objectKey": "authored-motion/1.0.0/page-critique/previews/3b1a01d923062cc9120dba4986cbb7947f5e924817565626ba08b176b4e74cb0.mp4",
3388
+ "sha256": "3b1a01d923062cc9120dba4986cbb7947f5e924817565626ba08b176b4e74cb0",
3389
+ "bytes": 1004547,
3390
+ "contentType": "video/mp4"
3391
+ },
3392
+ "poster": {
3393
+ "objectKey": "authored-motion/1.0.0/page-critique/previews/3715457c47c55e2a2f3756da16d6a03bae96c449562bc67a40a72894046c899f.jpg",
3394
+ "sha256": "3715457c47c55e2a2f3756da16d6a03bae96c449562bc67a40a72894046c899f",
3395
+ "bytes": 76938,
3396
+ "contentType": "image/jpeg"
3397
+ }
3398
+ },
3399
+ {
3400
+ "ratio": "tall",
3401
+ "width": 1080,
3402
+ "height": 1920,
3403
+ "duration": 13,
3404
+ "video": {
3405
+ "objectKey": "authored-motion/1.0.0/page-critique/previews/9fbefc8b49af2201cbabf930572bcff6184768cc13a44226d83041f8fa958a23.mp4",
3406
+ "sha256": "9fbefc8b49af2201cbabf930572bcff6184768cc13a44226d83041f8fa958a23",
3407
+ "bytes": 1381057,
3408
+ "contentType": "video/mp4"
3409
+ },
3410
+ "poster": {
3411
+ "objectKey": "authored-motion/1.0.0/page-critique/previews/b940972eb631f0e999285c7d1af8b9f75436c15f324ac0e9f03f407991f49c58.jpg",
3412
+ "sha256": "b940972eb631f0e999285c7d1af8b9f75436c15f324ac0e9f03f407991f49c58",
3413
+ "bytes": 114053,
3414
+ "contentType": "image/jpeg"
3415
+ }
3416
+ }
3417
+ ],
3418
+ "qa": {
3419
+ "status": "source-and-preview-hashes-verified",
3420
+ "previewCount": 3,
3421
+ "artisticApproval": "not-certified",
3422
+ "audioListened": false,
3423
+ "transferCertified": false
3424
+ },
3425
+ "restrictions": [
3426
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
3427
+ "Scene-specific data/count/layout limits are documented in the selected card.",
3428
+ "Opaque output only; no alpha export.",
3429
+ "Voice/music requires timeScale=1 until explicitly retimed.",
3430
+ "Inspect frame boundaries, typography and final audio before delivery."
3431
+ ]
3432
+ },
3433
+ {
3434
+ "id": "referral-tree",
3435
+ "version": "1.0.0",
3436
+ "title": "Una ra\xEDz, no una venta",
3437
+ "mechanism": "botanical-branching",
3438
+ "intent": "Crecimiento por referidos o efecto red con ramas muertas visibles; el contador suma lo que creci\xF3.",
3439
+ "niche": "negocios",
3440
+ "style": "ilustraci\xF3n",
3441
+ "duration": 11.5,
3442
+ "runtime": "canvas",
3443
+ "supportedRatios": [
3444
+ "16:9",
3445
+ "1:1",
3446
+ "9:16"
3447
+ ],
3448
+ "sourceSha256": "72eed1991f181017c7c6ba939e8d9e2c0726b2ad6ce05ee5143f7c452176a1bd",
3449
+ "cardSha256": "678a6c0cca9bb5a63c42714b08e9227d049d0a52a15103eb2ab266c1243e04bd",
3450
+ "card": '# referral-tree \xB7 Una ra\xEDz, no una venta\n\n**Mecanismo.** El crecimiento por referidos se dibuja como planta: el primer cliente es la ra\xEDz y en cada ronda crecen tallos hacia nuevos clientes (con hojas), seg\xFAn un patr\xF3n determinista. Los que no recomiendan quedan como ramas muertas visibles (grises, sin hojas) y una nota lo se\xF1ala. El contador suma exactamente los nodos que ya crecieron en pantalla.\n\n**\xDAsala para.** Negocio local (dentista, est\xE9tica, taller): "un cliente feliz trae a otros". Programa de referidos SaaS: "una cuenta se volvi\xF3 28 en 4 rondas". Creador/comunidad: crecimiento por boca a boca. Coaching: alumnos que recomiendan. **No la uses para.** Crecimiento pagado o por anuncios (no hay ramificaci\xF3n); proyecciones con tasas reales (el patr\xF3n es ilustrativo); organigramas o jerarqu\xEDas (se lee como crecimiento, no como estructura).\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| root | 0.3 | Se traza la l\xEDnea de suelo (0\u20130.8); la ra\xEDz aparece con n\xFAcleo `accent2`; el contador entra en 1. |\n| grow | 1.2 | Ronda 1: tallos hacia los `rootChildren` (0.9 s, desfase \u22640.25 s), brotan hojas, el nodo aparece al final del tallo y suma al contador; "ronda 1". |\n| step | 1.55 | No es un instante: intervalo entre rondas. La ronda g empieza en `grow` + (g\u22121)\xB7`step` (1.2, 2.75, 4.3, 5.85). La nota `dead` aparece junto a la primera rama muerta (~3.15 s). |\n| closing | 8.2 | Sale la nota (7.9\u20138.2); entra la frase de cierre (0.7 s). |\n| end | 11.5 | No lo usa el draw; ~2.6 s de cierre. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| `theme` / `brand` | kit (`sage`) y overrides | \u2014 |\n| `duration` `fps` `timeScale` | 11.5 s, 30 fps, 1 | \u2014 |\n| `rootChildren` | hijos de la ra\xEDz (ronda 1) | 1\u20135 legible |\n| `pattern[]` | hijos por nodo, en orden de ronda; se repite c\xEDclicamente | 0 = rama muerta (solo antes de la \xFAltima ronda); el total crece r\xE1pido: con >~20 nodos en la \xFAltima ronda se amontonan (las hojas del \xE1rbol se reparten el ancho) |\n| `generations` | n\xFAmero de rondas | cada ronda suma `step` s; con >4 mueve `closing`/`duration` |\n| `copy.round` | etiqueta de ronda, `{n}` = ronda calculada | corta (mono 24u) |\n| `copy.clients` / `copy.client` | etiqueta del contador, plural / singular | `client` solo cuando el contador vale 1 |\n| `copy.dead` | nota a mano en la primera rama muerta | 1 l\xEDnea; si `pattern` no tiene 0 no aparece |\n| `copy.closing` | frase final (display) | wrap a 0.24W en 16:9 (2\u20133 l\xEDneas), W\u2212140u en 1:1/9:16 |\n| `beats` / `sound.cues` | ver tabla y Sonido | \u2014 |\nCalculado: \xE1rbol, posiciones (hojas repartidas, padres centrados, \xB115/\xB120 u de irregularidad), ramas muertas, contador y ronda. Por defecto: 1+3+4+8+12 = 28 nodos, 3 ramas muertas.\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- `rootChildren`, `pattern`, `generations` (el total se recalcula solo), todo `copy`.\n- Voz: `grow` en la primera recomendaci\xF3n, `step` como cadencia de rondas, `closing` en la frase final.\n- Roles usados: `bg` (nodos muertos), `surface` (nodos), `ink` (tallos, suelo, contador, cierre), `muted` (nota, ronda), `accent` (anillos, hojas), `accent2` (ra\xEDz, mezcla de hojas). Fuentes: `display` (contador, cierre), `text` (`clients`), `mono` (ronda), `hand` (nota).\n- Los offsets de `paper` en `sound.cues` suponen `step` = 1.55: si cambias `step` o `generations`, recalc\xFAlalos a mano (`grow` + 0.9 + k\xB7`step`).\n\n## Qu\xE9 requiere tocar c\xF3digo\n- \xC1rbol desde una lista expl\xEDcita de nodos, o etiquetas/avatares por nodo.\n- Cues derivadas de los datos: la escena no define `cues(S)` (el runtime ya lo admite, como en zoom-niche y price-anatomy); habr\xEDa que a\xF1adirlo para que los `paper` sigan a `step`.\n- Varias ra\xEDces, \xE1rbol horizontal o hacia abajo.\n\n## Formatos\n- 16:9 \u2014 \xE1rbol a la derecha (0.32\u20130.95W, de 0.86H a 0.16H); contador 260u a la izquierda a media altura con etiquetas debajo; cierre en columna de 0.24W bajo el contador.\n- 1:1 \u2014 \xE1rbol a todo el ancho (0.08\u20130.92W, 0.78H\u21920.3H; suelo por encima del cierre); contador 170u arriba-izquierda con etiquetas a su derecha; cierre abajo (H\u221280u).\n- 9:16 \u2014 \xE1rbol 0.07\u20130.93W, 0.86H\u21920.26H; contador 220u arriba-izquierda; cierre abajo (H\u221280u).\n\n## Sonido\n`pop` en `root`+0.3 (la ra\xEDz), 4 `paper` en `grow` +0.9/+2.45/+4.0/+5.55 (cuando aparecen los nodos de cada ronda; offsets fijos que suponen `step` = 1.55), `swell` en `closing`. Funciona muda.\n\n## Limitaciones\n- El patr\xF3n es ilustrativo: el 28 es la suma de lo dibujado, no una proyecci\xF3n.\n- \xC1rboles grandes: nodos y hojas se tocan; los nodos de rondas altas se encogen (radio m\xEDnimo 11u).\n\n## Adaptar a otro tema\n- Cl\xEDnica est\xE9tica (`brand.colors` de la cl\xEDnica): `rootChildren` 2, `pattern` [2,1,0,2,3,1], `generations` 3 (total 11), `copy.clients` "pacientes", `copy.client` "paciente", `copy.closing` "Cada paciente feliz trae a otro.", `closing` \u22486.8.\n- SaaS de referidos (`theme: indigo`): `rootChildren` 3, `pattern` [3,2,0,1], `copy.round` "mes {n}", `copy.clients` "cuentas", `copy.client` "cuenta", `copy.dead` "no todos invitan", `copy.closing` "Cada cuenta activa es un canal.".\n- Comunidad de creador (`theme: night`): `copy.clients` "miembros", `copy.client` "miembro", `copy.round` "semana {n}", `step` 1.3 y offsets de `paper` recalculados (0.9, 2.2, 3.5, 4.8).\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Acumulaci\xF3n (el contador suma lo que creci\xF3) \u2190 Ror_Fly. Sin media externa.\n',
3451
+ "bundle": {
3452
+ "objectKey": "authored-motion/1.0.0/referral-tree/62511c55682a8ccc606e0279e74c9073b774e0101af110ce092bf13cd3f8b12a.tar.gz",
3453
+ "sha256": "62511c55682a8ccc606e0279e74c9073b774e0101af110ce092bf13cd3f8b12a",
3454
+ "bytes": 2934364,
3455
+ "fileCount": 58,
3456
+ "contentType": "application/gzip"
3457
+ },
3458
+ "previews": [
3459
+ {
3460
+ "ratio": "wide",
3461
+ "width": 1920,
3462
+ "height": 1080,
3463
+ "duration": 11.5,
3464
+ "video": {
3465
+ "objectKey": "authored-motion/1.0.0/referral-tree/previews/d0cfa150691cf659d1ea308fa4178866c40bd97ba8c7f361a8554bfb481d7d7e.mp4",
3466
+ "sha256": "d0cfa150691cf659d1ea308fa4178866c40bd97ba8c7f361a8554bfb481d7d7e",
3467
+ "bytes": 466555,
3468
+ "contentType": "video/mp4"
3469
+ },
3470
+ "poster": {
3471
+ "objectKey": "authored-motion/1.0.0/referral-tree/previews/3940f4bab9dc038cee33438325576436b5f3e68cf2d9f375989c3f5c258c8a3b.jpg",
3472
+ "sha256": "3940f4bab9dc038cee33438325576436b5f3e68cf2d9f375989c3f5c258c8a3b",
3473
+ "bytes": 106525,
3474
+ "contentType": "image/jpeg"
3475
+ }
3476
+ },
3477
+ {
3478
+ "ratio": "square",
3479
+ "width": 1080,
3480
+ "height": 1080,
3481
+ "duration": 11.5,
3482
+ "video": {
3483
+ "objectKey": "authored-motion/1.0.0/referral-tree/previews/7c574389154197fdfce06c148535596fbcd559bfa444d966aa206fbd3d3070ca.mp4",
3484
+ "sha256": "7c574389154197fdfce06c148535596fbcd559bfa444d966aa206fbd3d3070ca",
3485
+ "bytes": 432006,
3486
+ "contentType": "video/mp4"
3487
+ },
3488
+ "poster": {
3489
+ "objectKey": "authored-motion/1.0.0/referral-tree/previews/3e831daf49fe1ac6bad3e168a6507a550baa92378238d7d475013eb771ae8415.jpg",
3490
+ "sha256": "3e831daf49fe1ac6bad3e168a6507a550baa92378238d7d475013eb771ae8415",
3491
+ "bytes": 77683,
3492
+ "contentType": "image/jpeg"
3493
+ }
3494
+ },
3495
+ {
3496
+ "ratio": "tall",
3497
+ "width": 1080,
3498
+ "height": 1920,
3499
+ "duration": 11.5,
3500
+ "video": {
3501
+ "objectKey": "authored-motion/1.0.0/referral-tree/previews/e67695467170865d36e192242a619884f501ce3d5cff228ba026427c13053e8d.mp4",
3502
+ "sha256": "e67695467170865d36e192242a619884f501ce3d5cff228ba026427c13053e8d",
3503
+ "bytes": 498740,
3504
+ "contentType": "video/mp4"
3505
+ },
3506
+ "poster": {
3507
+ "objectKey": "authored-motion/1.0.0/referral-tree/previews/cbad242718bbdecec6e0081718e9f1dc53737f98a3e39c0451963a23488105ac.jpg",
3508
+ "sha256": "cbad242718bbdecec6e0081718e9f1dc53737f98a3e39c0451963a23488105ac",
3509
+ "bytes": 111720,
3510
+ "contentType": "image/jpeg"
3511
+ }
3512
+ }
3513
+ ],
3514
+ "qa": {
3515
+ "status": "source-and-preview-hashes-verified",
3516
+ "previewCount": 3,
3517
+ "artisticApproval": "not-certified",
3518
+ "audioListened": false,
3519
+ "transferCertified": false
3520
+ },
3521
+ "restrictions": [
3522
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
3523
+ "Scene-specific data/count/layout limits are documented in the selected card.",
3524
+ "Opaque output only; no alpha export.",
3525
+ "Voice/music requires timeScale=1 until explicitly retimed.",
3526
+ "Inspect frame boundaries, typography and final audio before delivery."
3527
+ ]
3528
+ },
3529
+ {
3530
+ "id": "brand-seal",
3531
+ "version": "1.0.0",
3532
+ "title": "Sello de marca",
3533
+ "mechanism": "stamp-seal-outro",
3534
+ "intent": "Cierre de video: anillo de marca que gira, sello estampado (logo propio opcional), tagline y CTA.",
3535
+ "niche": "cierre",
3536
+ "style": "marca",
3537
+ "duration": 6.5,
3538
+ "runtime": "canvas",
3539
+ "supportedRatios": [
3540
+ "16:9",
3541
+ "1:1",
3542
+ "9:16"
3543
+ ],
3544
+ "sourceSha256": "7c8d4f5e086235de11d68e184d1eb255cad44588c3081ac2659f97f152fe646e",
3545
+ "cardSha256": "cce54d64bed12ee66ba3477847b8d2ad6c34d24cdcc32af8277d5aabe6d97a62",
3546
+ "card": '# brand-seal \xB7 Sello de marca\n\n**Mecanismo.** Un anillo con el texto de la marca se traza y gira lento; el sello (monograma sobre disco `accent`, o el logo real si existe `media.logo`) cae desde fuera de c\xE1mara y se estampa en el centro con squash, salpicadura de tinta y golpe de c\xE1mara. Tras el impacto se imprimen tagline, CTA y handle: el video termina "sellado" con la marca y una acci\xF3n.\n\n**\xDAsala para.** Outro de restaurante o taquer\xEDa: tagline + "Pide en \u2026". Cierre de video de creador o marca personal con handle. Cierre de anuncio de tienda local con dominio. Firma de agencia al final de un caso de estudio. **No la uses para.** Hooks o aperturas (es un cierre de 6.5 s); logos horizontales muy alargados (se escalan por el lado mayor a 1.1R y quedan peque\xF1os); mensajes que necesitan explicar algo.\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| ring | 0.1 | Anillo exterior se traza en sentido horario y el interior al rev\xE9s (0.8 s); el texto del anillo aparece (0.3\u20131.0) y gira 0.35 rad/s todo el plano. |\n| stamp | 1.05 | El sello cae (escala 2.4\u21921, 0.7\u20131.05) y golpea: squash, golpe de c\xE1mara, 18 gotas de tinta `accent` que se desvanecen (1.65\u20133.05). |\n| tagline | 1.8 | La tagline sube y aparece (0.6 s). |\n| cta | 2.5 | La p\xEDldora CTA (`ink`) aparece con rebote (0.5 s); el handle entra en la segunda mitad. |\n| end | 6.5 | No lo usa el draw; ~3.5 s de cierre con el anillo girando. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| `theme` / `brand` | kit (`trattoria`) y overrides | \u2014 |\n| `duration` `fps` `timeScale` | 6.5 s, 30 fps, 1 | \u2014 |\n| `media.logo` | logo propio (ruta relativa a scene.html) | opcional (`media` vac\xEDo por defecto); reemplaza disco + monograma y conserva sus colores; lado mayor = 1.1R; usar PNG con alfa |\n| `copy.ring` | frase del anillo; se repite y se estira/comprime para cerrar el c\xEDrculo | terminar en " \xB7 "; ideal \u2248 1/2 o 1/3 de la circunferencia (\u22481350 px en 16:9 a heavy 0.11R): 31 car. se repiten 2\xD7; si cabe menos de 2 veces sale 1 vez con letras muy separadas; muy larga se comprime |\n| `copy.monogram` | letra sobre el disco (sin logo) | 1\u20132 car. (display 0.78R) |\n| `copy.tagline` | display, con wrap; CTA y handle bajan seg\xFAn las l\xEDneas | \u22642 l\xEDneas en 1:1 (con 3 el handle sale por abajo); 3 caben en 16:9 y 9:16; el default ocupa 2 en 16:9 |\n| `copy.cta` | p\xEDldora heavy | sin wrap; ancho = texto + 70u |\n| `copy.handle` | mono `muted` bajo la p\xEDldora | 1 l\xEDnea |\n| `beats` / `sound.cues` | ver tabla y Sonido | \u2014 |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- Todo `copy`; `media.logo` para estampar el logo real.\n- Voz/m\xFAsica: `stamp` en el golpe, `tagline` y `cta` en las frases de cierre.\n- Roles usados: `bg`, `surface` (texto del CTA y candidato del monograma), `ink` (texto del anillo, tagline, p\xEDldora), `muted` (handle), `accent` (anillos, disco, tinta). `accent2` no se usa. Fuentes: `heavy` (anillo, CTA), `display` (monograma, tagline), `mono` (handle). Grano fijo de 3.5 %.\n\n## Qu\xE9 requiere tocar c\xF3digo\n- Tagline de 3+ l\xEDneas en 1:1 (requiere subir el sello o achicar el texto).\n- M\xE1s de un CTA, quitar grano o golpe de c\xE1mara, color del texto del anillo.\n- Anillo sin texto o con logo repetido.\n\n## Formatos\n- 16:9 \u2014 sello a la izquierda (0.32W, 0.5H), R 250u; textos alineados a la izquierda a la derecha del sello (x = sello + R + 120u, 0.46H).\n- 1:1 \u2014 sello centrado (0.5W, 0.4H), R 230u; textos centrados debajo (+120u).\n- 9:16 \u2014 sello (0.5W, 0.38H), R 300u; textos centrados debajo (+180u).\n\n## Sonido\n`whoosh-short` en `ring` (se traza el anillo), `stamp` en `stamp` (impacto), `paper` en `tagline`, `pop` en `cta`. Funciona muda.\n\n## Limitaciones\n- Tagline larga empuja CTA y handle hacia abajo (en 1:1 salen del cuadro con 3 l\xEDneas); CTA largo puede salirse por el costado (sin wrap).\n- El espaciado de letras del anillo depende del largo de la frase: frases que no encajan \u2248 un divisor de la circunferencia se ven sueltas o apretadas.\n- Logo raster sin alfa muestra su fondo como rect\xE1ngulo.\n- Grano y golpe de c\xE1mara pueden chocar con marcas minimalistas.\n- Copy por defecto (Taquer\xEDa Lupita, lupita.mx, @taquerialupita) es de ejemplo, no un cliente.\n\n## Adaptar a otro tema\n- Estudio de yoga (`theme: sage`): `copy.ring` "ESTUDIO RA\xCDZ \xB7 YOGA \xB7 DESDE 2015 \xB7 ", `copy.monogram` "R", `copy.tagline` "Respira antes de correr.", `copy.cta` "Clase gratis en raiz.mx", `copy.handle` "@estudioraiz".\n- Agencia (`theme: ledger` + `media.logo`): `media` {"logo": "media/logo.png"}, `copy.ring` "NORTE STUDIO \xB7 BRANDING \xB7 WEB \xB7 ", `copy.tagline` "Marcas que se recuerdan.", `copy.cta` "Agenda una llamada", `copy.handle` "norte.studio".\n- Cafeter\xEDa (`theme: cafe`): `copy.ring` "CAF\xC9 ALTURA \xB7 TUESTE PROPIO \xB7 ", `copy.monogram` "A", `copy.tagline` "Tostado esta semana.", `copy.cta` "Pide en altura.cafe".\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Mecanismo original. Sin media por defecto; si se usa `media.logo`, es material del cliente.\n',
3547
+ "bundle": {
3548
+ "objectKey": "authored-motion/1.0.0/brand-seal/96b7de66ab7d1737e58aaeee1b73f71319472d25731c3bc461198c62d9a6f504.tar.gz",
3549
+ "sha256": "96b7de66ab7d1737e58aaeee1b73f71319472d25731c3bc461198c62d9a6f504",
3550
+ "bytes": 2933366,
3551
+ "fileCount": 58,
3552
+ "contentType": "application/gzip"
3553
+ },
3554
+ "previews": [
3555
+ {
3556
+ "ratio": "wide",
3557
+ "width": 1920,
3558
+ "height": 1080,
3559
+ "duration": 6.5,
3560
+ "video": {
3561
+ "objectKey": "authored-motion/1.0.0/brand-seal/previews/b4b9cc5880dbc5a5b7ef3695e4dc6e0b69275d011c0af66073bc2ee3fc3503ef.mp4",
3562
+ "sha256": "b4b9cc5880dbc5a5b7ef3695e4dc6e0b69275d011c0af66073bc2ee3fc3503ef",
3563
+ "bytes": 18690128,
3564
+ "contentType": "video/mp4"
3565
+ },
3566
+ "poster": {
3567
+ "objectKey": "authored-motion/1.0.0/brand-seal/previews/14cffd40c38090a49bc1ea6808d746ff4235af6cf94a5f60fc42beb597c28f03.jpg",
3568
+ "sha256": "14cffd40c38090a49bc1ea6808d746ff4235af6cf94a5f60fc42beb597c28f03",
3569
+ "bytes": 237333,
3570
+ "contentType": "image/jpeg"
3571
+ }
3572
+ },
3573
+ {
3574
+ "ratio": "square",
3575
+ "width": 1080,
3576
+ "height": 1080,
3577
+ "duration": 6.5,
3578
+ "video": {
3579
+ "objectKey": "authored-motion/1.0.0/brand-seal/previews/b838f2394d1fc39d97727914ed49763c91df1052664f3b527b17713e73c584a3.mp4",
3580
+ "sha256": "b838f2394d1fc39d97727914ed49763c91df1052664f3b527b17713e73c584a3",
3581
+ "bytes": 13063851,
3582
+ "contentType": "video/mp4"
3583
+ },
3584
+ "poster": {
3585
+ "objectKey": "authored-motion/1.0.0/brand-seal/previews/a70791b2db0d28479208df8afa8e952c43c752da525d6257bb965a1cde7ef4da.jpg",
3586
+ "sha256": "a70791b2db0d28479208df8afa8e952c43c752da525d6257bb965a1cde7ef4da",
3587
+ "bytes": 150818,
3588
+ "contentType": "image/jpeg"
3589
+ }
3590
+ },
3591
+ {
3592
+ "ratio": "tall",
3593
+ "width": 1080,
3594
+ "height": 1920,
3595
+ "duration": 6.5,
3596
+ "video": {
3597
+ "objectKey": "authored-motion/1.0.0/brand-seal/previews/1074309975da9ea31eb0eae26b522a6ce5bf15db24db5ba1b79c19bb2f08a5b1.mp4",
3598
+ "sha256": "1074309975da9ea31eb0eae26b522a6ce5bf15db24db5ba1b79c19bb2f08a5b1",
3599
+ "bytes": 23509646,
3600
+ "contentType": "video/mp4"
3601
+ },
3602
+ "poster": {
3603
+ "objectKey": "authored-motion/1.0.0/brand-seal/previews/fe89c557283a10baa2dd7c8fd4a28d6826709301d20c321e42257adcbf311a65.jpg",
3604
+ "sha256": "fe89c557283a10baa2dd7c8fd4a28d6826709301d20c321e42257adcbf311a65",
3605
+ "bytes": 243015,
3606
+ "contentType": "image/jpeg"
3607
+ }
3608
+ }
3609
+ ],
3610
+ "qa": {
3611
+ "status": "source-and-preview-hashes-verified",
3612
+ "previewCount": 3,
3613
+ "artisticApproval": "not-certified",
3614
+ "audioListened": false,
3615
+ "transferCertified": false
3616
+ },
3617
+ "restrictions": [
3618
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
3619
+ "Scene-specific data/count/layout limits are documented in the selected card.",
3620
+ "Opaque output only; no alpha export.",
3621
+ "Voice/music requires timeScale=1 until explicitly retimed.",
3622
+ "Inspect frame boundaries, typography and final audio before delivery."
3623
+ ]
3624
+ },
3625
+ {
3626
+ "id": "chat-closer",
3627
+ "version": "1.0.0",
3628
+ "title": "El asistente que califica",
3629
+ "mechanism": "data-extraction-to-crm",
3630
+ "intent": "Automatizaci\xF3n de ventas: respuestas de un chat salen como datos y arman un lead calificado.",
3631
+ "niche": "ventas",
3632
+ "style": "UI",
3633
+ "duration": 13,
3634
+ "runtime": "canvas",
3635
+ "supportedRatios": [
3636
+ "16:9",
3637
+ "1:1",
3638
+ "9:16"
3639
+ ],
3640
+ "sourceSha256": "c277b2588b3c767cac75e00d540004073052e1894b1a497bc193db5c7cd52344",
3641
+ "cardSha256": "77372d3439561bee05783568189c4787e20c3734f8b5ee74da20fff6cd86af90",
3642
+ "card": '# chat-closer \xB7 El lead que se calific\xF3 solo\n\n**Mecanismo.** Un chat gen\xE9rico a las 23:47: el cliente pregunta precio y el asistente hace dos preguntas con respuestas r\xE1pidas que se tocan. Cada dato clave (palabras marcadas con `{\u2026}` o la respuesta elegida) se subraya, se desprende de su burbuja como chip y vuela a una ficha de CRM junto al chat (debajo en 9:16). El primer chip que aterriza vuelve s\xF3lida la ficha fantasma (wipe circular desde su lugar); el nombre se desenvuelve en t\xEDtulo, cada campo recibe check, la barra `n/total` se llena y el estado pasa de "Calificando\u2026" a "Calificado \xB7 Visita \u2026" por wipe. Cierra con el sello "Listo" mientras el chat se oscurece. Protagonista: los chips de datos (palabra \u2192 chip \u2192 campo).\n\n**\xDAsala para.** Negocios que venden por chat: muebler\xEDa o showroom (agendar visita), inmobiliaria (cita para ver un depa), cl\xEDnica (agendar valoraci\xF3n), escuela o curso (llamada de admisi\xF3n), agencia de autos (prueba de manejo). **No la uses para.** Conversaciones de m\xE1s de 4 mensajes o m\xE1s de 2 preguntas con botones; mostrar WhatsApp o un CRM real (UI gen\xE9rica ilustrativa); bots que no recogen datos.\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| (inicio) | 0\u20130.8 | El chat sube (0\u20130.55); pastilla `today` a 0.15; la ficha fantasma (borde punteado) entra 0.25\u20130.8. |\n| m1 | 0.6 | Mensaje del cliente. El reloj del status bar avanza de `clockStart` a +`clockMinutes` entre m1 y m4. |\n| lift1 | 1.2 | Subrayado bajo los spans (\u22120.42\u2026\u22120.12) y despegue, un chip cada 0.3 s; aterrizan a +0.72. El primero inunda la ficha de `surface`; el nombre se convierte en t\xEDtulo (28u). |\n| m2 | 2.9 | Bot: indicador "escribiendo" desde \u22120.75; respuestas r\xE1pidas a +0.35. |\n| tap1 | 4.0 | Dedo + ripple sobre `replies[pick]`; el chip se transforma en burbuja del cliente (0.45 s). |\n| lift2 | 4.6 | Esa respuesta vuela a su campo. |\n| m3 \xB7 tap2 \xB7 lift3 | 5.8 \xB7 6.75 \xB7 7.35 | Lo mismo con la 2.\xAA pregunta. |\n| m4 | 8.3 | Mensaje final del bot (texto libre, no se calcula). |\n| status | 8.95 | Wipe izquierda\u2192derecha a `pos` con `statusDone`; +0.35 aparece `created` con la hora final. |\n| stamp | 10.05 | Sello (2.4\u21921 en 0.14 s) con rebote de la ficha; el chat se oscurece 62 %; push-in a la ficha (4.5 % en 16:9, 3.5 % en 9:16, 0 en 1:1). |\n| end | 13 | No lo usa el c\xF3digo; ~2.9 s de hold. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand, duration, fps, timeScale | kit `chat`, 13 s | \u2014 |\n| copy.store / storeSub | nombre y subt\xEDtulo del header | maxW ~290 cu (solo avisa) |\n| copy.today / composer | pastilla de fecha / placeholder del campo de texto | \u2014 |\n| copy.clockStart / clockMinutes | "HH:MM" + minutos que avanza el reloj; de aqu\xED salen la hora de cada burbuja y `{time}` | entero; avanza por `floor`, no por evento |\n| copy.messages[] | `{from: "client"\\|"bot", text, keys?, replies?, pick?, key?}`. En `text`, `{\u2026}` marca un dato (no se parte de l\xEDnea; las llaves no se ven); `keys` nombra esos spans en orden; `replies`+`pick`+`key` = respuesta r\xE1pida que se vuelve dato | m\xE1x. 4 mensajes (m1\u2013m4), m\xE1x. 2 con `replies` (tap1\u20132), m\xE1x. 3 despegues (lift1\u20133: uno por mensaje con `keys`, uno por `replies` con `key`). M\xE1s `keys` que spans \u2192 error; spans sin key no despegan. Burbuja \u2264 281 cu de ancho |\n| copy.nameKey | qu\xE9 dato es el nombre (t\xEDtulo + inicial del avatar) | debe existir; \u2264 ~9 car. (maxW 130 cu a 28u) |\n| copy.fields | `[[key, etiqueta], \u2026]` filas de la ficha | 3: el alto de ficha es fijo (418 cu) y con 4 el estado se sale. Cada key debe existir o la escena falla; un dato que no est\xE1 en `fields` ni es `nameKey` aterriza encima del header |\n| copy.cardKicker / namePlaceholder | kicker mono (maxW 220 cu) / texto gris antes del nombre | \u2014 |\n| copy.progress | `{n}` datos aterrizados, `{total}` = n\xFAmero de datos | \u2014 |\n| copy.statusPending / statusDone | `{dia}` = texto del dato con key **literal `dia`**, primera letra en min\xFAscula | si tu key no se llama `dia`, queda vac\xEDo |\n| copy.stamp / created / note | sello (MAY\xDAS) / pie de ficha (`{time}` = reloj final) / aviso abajo-derecha | \u2014 |\n| valores de datos | chip = texto + 28 cu | \u2264 ~20 car. (chip \u2264 214 cu; warning `chip too wide for card`) |\n| beats.* | ver tabla | orden m \u2192 tap \u2192 lift; dejar \u2265 0.4 s entre un tap y su lift |\n| sound.cues | 16 cues manuales | los snaps de lift1 (+0.72 / +1.02) asumen 2 datos en el primer mensaje |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- Negocio, conversaci\xF3n, opciones, datos, campos (3), textos de estado y sello, hora de inicio.\n- Beats con la voz: `m1` en "a las 11 de la noche\u2026", `tap1/tap2` en "elige", `status` en "calificado", `stamp` en "listo".\n- Roles: `accent` (avatar, bot\xF3n enviar, tinte de burbujas del cliente, luz detr\xE1s de la ficha), `accent2` (subrayado, chips, respuestas, palomitas de le\xEDdo, avatar del lead), `pos` (en l\xEDnea, checks, progreso, estado, sello), `surface` (header, burbujas del bot, ficha), `line`, `muted`, `ink`, `bg` (oscurecido final). Fuentes: `text`, `heavy`, `mono`.\n\n## Qu\xE9 requiere tocar c\xF3digo\n- Icono de sof\xE1 del avatar del header (dibujado, espec\xEDfico de muebler\xEDa) y la luna junto al reloj.\n- M\xE1s mensajes, preguntas o datos (`msgB/tapB/liftB`); 4+ campos (`ROW0`, `ROWH`, alto de ficha).\n- Desacoplar `{dia}` de la key `dia`; respuestas r\xE1pidas en mensajes del cliente; fotos, audios o stickers.\n\n## Formatos\n- 16:9: chat (k = 1.7, 646 px) a la izquierda y ficha a la derecha, separaci\xF3n 90 px; ventana de mensajes \u2248 421 cu.\n- 1:1: k = 1.3, separaci\xF3n 24 px, ~34 px de margen lateral; ventana \u2248 625 cu; sin push-in final.\n- 9:16: k = 1.95; chat arriba (\u2248 885 px de alto, ventana \u2248 310 cu: hace scroll antes) y ficha centrada abajo; los chips vuelan en curva por la derecha.\n\n## Sonido\npop en m1; por despegue whoosh-short + snap al aterrizar; blip por mensaje del bot; click en cada tap; success en `status` (+0.2); stamp + swell en el sello. Funciona muda: el subrayado y el vuelo cuentan qu\xE9 se captur\xF3.\n\n## Limitaciones\n- UI ilustrativa (no es WhatsApp ni un CRM real); tienda, persona y precios ficticios. Mantener `copy.note`.\n- Estructura fija 4 mensajes / 2 preguntas / 3 despegues; el scroll es autom\xE1tico y en 9:16 el primer mensaje sale de vista antes de m4.\n- Los `maxW` solo avisan en `getDiagnostics()`; no hay ajuste de texto fuera de las burbujas.\n- `statusDone` depende del nombre de key `dia`.\n\n## Adaptar a otro tema\n- Inmobiliaria (`theme: estate`): `store "Casa Norte"`, `messages[0].text "Hola, soy {Rodrigo}. \xBFSigue disponible el {depa en Roma Norte}?"`, `replies ["Hasta $3M","$3M \u2013 $5M","M\xE1s de $5M"]` (pick 1, key `presupuesto`), `replies ["Ma\xF1ana 17:00","S\xE1bado 11:00","Otro d\xEDa"]` (pick 1, key `dia`), `m4 "\xA1Listo! Te esperamos el s\xE1bado a las 11:00."`. El sof\xE1 del avatar sigue (c\xF3digo).\n- Cl\xEDnica dental (`theme: medical`): `store "Cl\xEDnica Sonrisa"`, `text "Hola, soy {Ana}. \xBFCu\xE1nto cuesta un {blanqueamiento}?"`, `fields [["busca","Tratamiento"],["presupuesto","Presupuesto"],["dia","Cita"]]`, `statusDone "Calificada \xB7 Cita {dia}"`, `stamp "Agendada"`.\n- Escuela de ingl\xE9s (`theme: indigo`): `text "Hola, soy {Luis}. Info del {curso intensivo}"`, presupuesto `["$1,500/mes","$2,500/mes","Beca"]`, `dia ["Hoy 18:00","Ma\xF1ana 9:00","Otro d\xEDa"]`, `statusDone "Calificado \xB7 Llamada {dia}"`.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Sin media (no hay `params.media`): chat, avatar, ficha e iconos se dibujan con c\xF3digo.\n',
3643
+ "bundle": {
3644
+ "objectKey": "authored-motion/1.0.0/chat-closer/b10b16835ea4160c1ebba858d5a1e0e6d30be16de128a5dc4d4cc7984776dda4.tar.gz",
3645
+ "sha256": "b10b16835ea4160c1ebba858d5a1e0e6d30be16de128a5dc4d4cc7984776dda4",
3646
+ "bytes": 2942508,
3647
+ "fileCount": 58,
3648
+ "contentType": "application/gzip"
3649
+ },
3650
+ "previews": [
3651
+ {
3652
+ "ratio": "wide",
3653
+ "width": 1920,
3654
+ "height": 1080,
3655
+ "duration": 13,
3656
+ "video": {
3657
+ "objectKey": "authored-motion/1.0.0/chat-closer/previews/ed34677db24ca9dc33959a9487b9e1e08b7024b275e425f205fbf6f09b649b0b.mp4",
3658
+ "sha256": "ed34677db24ca9dc33959a9487b9e1e08b7024b275e425f205fbf6f09b649b0b",
3659
+ "bytes": 2503757,
3660
+ "contentType": "video/mp4"
3661
+ },
3662
+ "poster": {
3663
+ "objectKey": "authored-motion/1.0.0/chat-closer/previews/67c1ef27b3dca140ca48818f647c61f87cb20d0fa96c58df30400585c7974747.jpg",
3664
+ "sha256": "67c1ef27b3dca140ca48818f647c61f87cb20d0fa96c58df30400585c7974747",
3665
+ "bytes": 142663,
3666
+ "contentType": "image/jpeg"
3667
+ }
3668
+ },
3669
+ {
3670
+ "ratio": "square",
3671
+ "width": 1080,
3672
+ "height": 1080,
3673
+ "duration": 13,
3674
+ "video": {
3675
+ "objectKey": "authored-motion/1.0.0/chat-closer/previews/b2b15b6f01d29f1e975dd0f22575f782a869a7c88c5f03754f5e51117d3a9b0b.mp4",
3676
+ "sha256": "b2b15b6f01d29f1e975dd0f22575f782a869a7c88c5f03754f5e51117d3a9b0b",
3677
+ "bytes": 1313137,
3678
+ "contentType": "video/mp4"
3679
+ },
3680
+ "poster": {
3681
+ "objectKey": "authored-motion/1.0.0/chat-closer/previews/eead5538e93b2e90e649dec917361b7e00f4c902e136634339606e655f2bdff6.jpg",
3682
+ "sha256": "eead5538e93b2e90e649dec917361b7e00f4c902e136634339606e655f2bdff6",
3683
+ "bytes": 99950,
3684
+ "contentType": "image/jpeg"
3685
+ }
3686
+ },
3687
+ {
3688
+ "ratio": "tall",
3689
+ "width": 1080,
3690
+ "height": 1920,
3691
+ "duration": 13,
3692
+ "video": {
3693
+ "objectKey": "authored-motion/1.0.0/chat-closer/previews/9bbc8d471bf32c91554811e73f66389fb416428df98c8c082b8e208c052fd73e.mp4",
3694
+ "sha256": "9bbc8d471bf32c91554811e73f66389fb416428df98c8c082b8e208c052fd73e",
3695
+ "bytes": 2568299,
3696
+ "contentType": "video/mp4"
3697
+ },
3698
+ "poster": {
3699
+ "objectKey": "authored-motion/1.0.0/chat-closer/previews/21004d9823c41999b7d28f3738ac9179eb2519219e585892b1487ebcddfc494d.jpg",
3700
+ "sha256": "21004d9823c41999b7d28f3738ac9179eb2519219e585892b1487ebcddfc494d",
3701
+ "bytes": 152847,
3702
+ "contentType": "image/jpeg"
3703
+ }
3704
+ }
3705
+ ],
3706
+ "qa": {
3707
+ "status": "source-and-preview-hashes-verified",
3708
+ "previewCount": 3,
3709
+ "artisticApproval": "not-certified",
3710
+ "audioListened": false,
3711
+ "transferCertified": false
3712
+ },
3713
+ "restrictions": [
3714
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
3715
+ "Scene-specific data/count/layout limits are documented in the selected card.",
3716
+ "Opaque output only; no alpha export.",
3717
+ "Voice/music requires timeScale=1 until explicitly retimed.",
3718
+ "Inspect frame boundaries, typography and final audio before delivery."
3719
+ ]
3720
+ },
3721
+ {
3722
+ "id": "night-sales",
3723
+ "version": "1.0.0",
3724
+ "title": "Mientras dorm\xEDas",
3725
+ "mechanism": "time-lapse-notification-stack",
3726
+ "intent": "E-commerce: la noche pasa en el reloj, los pedidos se apilan y colapsan en un resumen calculado.",
3727
+ "niche": "e-commerce",
3728
+ "style": "UI",
3729
+ "duration": 12,
3730
+ "runtime": "canvas",
3731
+ "supportedRatios": [
3732
+ "16:9",
3733
+ "1:1",
3734
+ "9:16"
3735
+ ],
3736
+ "sourceSha256": "3be245ba5fcb0229744a578ca1f17e2bfc506175ffdafe54a969367fa84bd264",
3737
+ "cardSha256": "b2f1c8642a0ca5a7d6e8e0776cd6fa42492e9af95b719c9b36dd84116e98db24",
3738
+ "card": '# night-sales \xB7 Vendiste mientras dorm\xEDas\n\n**Mecanismo.** Una pantalla de bloqueo hace time-lapse de `night.from` a `night.to`. El reloj es el protagonista: mueve el cielo de noche a amanecer (el brillo sube desde el horizonte, sin cross-fade), apaga las estrellas y saca el sol en la \xFAltima hora. Cada pedido llega como notificaci\xF3n exactamente en su minuto de reloj (las llegadas son la inversa de la curva del time-lapse); las viejas se pliegan en "+N m\xE1s". En 16:9/1:1 un total corre al lado y unas barras por hora crecen con cada pedido. A la hora `to` suena la alarma: el reloj corta a `accent`, tiembla y el tel\xE9fono vibra; la pila colapsa en una notificaci\xF3n "{n} pedidos nuevos" que sale del tel\xE9fono y se vuelve la tarjeta "Mientras dorm\xEDas". Total, conteos y barras se calculan de `orders`.\n\n**\xDAsala para.** Ecommerce (pedidos nocturnos), cursos o productos digitales (ventas de madrugada), hotel o reservas en l\xEDnea, SaaS (altas mientras duermes), agencia de anuncios mostrando una campa\xF1a que vende sola. **No la uses para.** Ventanas que no terminen de ma\xF1ana (el cielo siempre amanece al final), montos con decimales o moneda como sufijo, mostrar la app real de una tienda, kits claros (el cielo necesita kit oscuro).\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| phone | 0 | El tel\xE9fono sube (0.8 s); el panel aparece 0.35\u20130.95 con $0 (16:9/1:1). En 9:16 el widget entra en lapse \xB1 0.3. |\n| lapse | 0.9 | Time-lapse `inOutSine` hasta `alarm`: lento al inicio y al final, r\xE1pido a media noche. Cada pedido entra en 0.3 s; total y barras suben en 0.4 s; el total da un "kick" por pedido; un cursor marca la hora sobre las barras. Al cruzar 00:00, `dateA` \u2192 `dateB` (flip). Amanecer desde el 56 % del intervalo; sol en los \xFAltimos 55 min. |\n| alarm | 8.3 | Reloj en `to`: corte a accent, wobble, 3 anillos, pastilla "Alarma \xB7 07:00"; vibraci\xF3n del tel\xE9fono (16:9/1:1). |\n| collapse | 8.85 | Las notificaciones se juntan en el lugar superior (0.45 s); +0.3 aparece la fusionada. |\n| card | 9.45 | 16:9/1:1: la fusionada vuela y crece hasta la tarjeta detr\xE1s del panel (0.65 s); la etiqueta pasa a `summary` y el subt\xEDtulo a "{n} pedidos \xB7 from\u2013to". 9:16: crece en el lugar del widget (300 cu); el total se acopla del widget al titular (18\u219264) y las barras crecen escalonadas desde +0.5. |\n| end | 12 | No lo usa el c\xF3digo; ~2.5 s de hold sobre el amanecer. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand, duration, fps, timeScale | kit `glass`, 12 s | el cielo usa `bg`, `surface`, `accent`, `series[3]`, `series[4]`: kit oscuro y `series` \u2265 5 |\n| currency | prefijo del monto | enteros con coma de miles; sin decimales |\n| night.from / to | "HH:MM"; puede cruzar medianoche | \u2264 24 h; barras = horas redondeadas hacia arriba; etiquetas = hora de `from` + h (si `from` no es en punto, redondean abajo) |\n| orders | `[hora, monto, ciudad]`; se ordenan por minuto | un pedido fuera de la ventana llega en `alarm` y no entra en las barras (s\xED en el total); 14 c\xF3modo; pedidos del mismo minuto se enciman al entrar |\n| copy.dateA / dateB | fechas libres (no se calculan) | el flip es al cruzar 00:00; si la ventana no cruza medianoche, ocurre en `alarm` |\n| copy.app | etiqueta MAY\xDAS de la notificaci\xF3n fusionada | \u2014 |\n| copy.orderTitle | `{amount}` = monto formateado | maxW \u2248 210 cu; ciudad en 1 l\xEDnea |\n| copy.more / merged | `{n}` = llegados \u2212 3 / total de pedidos | \u2014 |\n| copy.alarm | pastilla de alarma (se le agrega " \xB7 {to}") | \u2014 |\n| copy.panelNight / widget | etiqueta del panel (16:9/1:1) / del widget (9:16) | \u2014 |\n| copy.summary / count / note | titular del resumen / `{n} pedidos` / aviso abajo-derecha | \u2014 |\n| beats | ver tabla | `alarm` es el final del time-lapse |\n| sound.perOrder | `{sfx, gain}`: se agrega un cue por pedido en su llegada | si existe, `sound.cues` tambi\xE9n debe existir (se le hace push) |\n| sound.cues | 9 cues | \u2014 |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- Pedidos, ventana horaria, moneda, fechas y todos los textos; total, barras y "+N" se recalculan.\n- Beats con la voz: `alarm` en "a las 7", `card` en la cifra.\n- Roles: `accent` (amanecer, sol, alarma, icono, barras, t\xEDtulo del resumen), `series[3]`/`series[4]` (tonos del cielo), `surface` (vidrio de notificaciones), `bg` (noche, velo), `ink`, `muted`, `line` (borde del tel\xE9fono). Fuentes: `display` (reloj, totales), `text`, `heavy`, `mono`.\n\n## Qu\xE9 requiere tocar c\xF3digo\n- Icono de bolsa de compra en cada notificaci\xF3n (ecommerce); linterna, c\xE1mara, isla din\xE1mica, colinas y estrellas.\n- Montos decimales o moneda como sufijo; agrupar pedidos por notificaci\xF3n; m\xE1s de 2 notificaciones completas visibles.\n- Otro tipo de cielo (d\xEDa\u2192noche, sin amanecer).\n\n## Formatos\n- 16:9: tel\xE9fono de 980u de alto centrado al 32 % W; panel a 54.5 % W (680u, total hasta 160u con fit); el mismo cielo llena la sala, velado 52 %.\n- 1:1: tel\xE9fono al 25.5 % W; panel a 52.5 % W (450u, total \u2264 104u).\n- 9:16: la pantalla de bloqueo es todo el cuadro (sin cuerpo de tel\xE9fono ni isla); widget con total y conteo en vivo bajo el reloj; sin panel; el resumen se forma en el lugar del widget y deja ver el amanecer abajo.\n\n## Sonido\nswell en `lapse`; un `blip` por pedido (generado); chime + 2 ticks en `alarm`; whoosh-short + snap (+0.42) en `collapse`; whoosh, coin (+0.6) y swell (+0.8) en `card`. Funciona muda.\n\n## Limitaciones\n- Tienda, ciudades y montos ilustrativos; mantener `copy.note`.\n- La curva hace que los pedidos de media noche pasen muy r\xE1pido: si hay varios entre 02:00 y 04:00 se leen poco.\n- Notificaciones de alto fijo (66 cu), sin wrap.\n- El amanecer ocurre al final de cualquier ventana: una ventana 20:00\u201302:00 amanecer\xEDa a las 2.\n\n## Adaptar a otro tema\n- Curso digital (`theme: night`): `copy.app "Academia Norte"`, `orderTitle "Nueva inscripci\xF3n \xB7 {amount}"`, `merged "{n} inscripciones"`, `count "{n} alumnos"`, `panelNight "Inscripciones de esta noche"`, `orders` con `["23:12", 499, "Bogot\xE1"]`, `["01:40", 499, "Lima"]`\u2026\n- Hotel boutique (`theme: noir`): `copy.app "Casa Marea"`, `orderTitle "Reserva \xB7 {amount}"`, ciudad = origen del hu\xE9sped, `currency "$"`, `night {"from": "22:00", "to": "07:00"}`, `summary "Reservas de madrugada"`.\n- SaaS (`theme: blueprint`): `orderTitle "Nuevo cliente \xB7 {amount}/mes"`, `count "{n} altas"`, `widget "Altas esta noche"`, `dateA/dateB` seg\xFAn el d\xEDa del lanzamiento.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Sin media (no hay `params.media`): tel\xE9fono, cielo e iconos se dibujan con c\xF3digo.\n',
3739
+ "bundle": {
3740
+ "objectKey": "authored-motion/1.0.0/night-sales/9eb74889c3834ebac8a8bc1a7994ad91f1d5fb2676c2f65dbabc87c361f00495.tar.gz",
3741
+ "sha256": "9eb74889c3834ebac8a8bc1a7994ad91f1d5fb2676c2f65dbabc87c361f00495",
3742
+ "bytes": 2940366,
3743
+ "fileCount": 58,
3744
+ "contentType": "application/gzip"
3745
+ },
3746
+ "previews": [
3747
+ {
3748
+ "ratio": "wide",
3749
+ "width": 1920,
3750
+ "height": 1080,
3751
+ "duration": 12,
3752
+ "video": {
3753
+ "objectKey": "authored-motion/1.0.0/night-sales/previews/d3cdc3d735b3d0861265cbe7581af27adbcc5b5b59e9b0b7b9dec4d68e859f3e.mp4",
3754
+ "sha256": "d3cdc3d735b3d0861265cbe7581af27adbcc5b5b59e9b0b7b9dec4d68e859f3e",
3755
+ "bytes": 2916162,
3756
+ "contentType": "video/mp4"
3757
+ },
3758
+ "poster": {
3759
+ "objectKey": "authored-motion/1.0.0/night-sales/previews/51db5c2b491c934abde993b395324dc19bafffc2fed47bc3aa6b52db9609c249.jpg",
3760
+ "sha256": "51db5c2b491c934abde993b395324dc19bafffc2fed47bc3aa6b52db9609c249",
3761
+ "bytes": 120287,
3762
+ "contentType": "image/jpeg"
3763
+ }
3764
+ },
3765
+ {
3766
+ "ratio": "square",
3767
+ "width": 1080,
3768
+ "height": 1080,
3769
+ "duration": 12,
3770
+ "video": {
3771
+ "objectKey": "authored-motion/1.0.0/night-sales/previews/8a1cbeee810ab1ab11d7ba74dca355af6ae02143f46d13d2f09f4562644c00f1.mp4",
3772
+ "sha256": "8a1cbeee810ab1ab11d7ba74dca355af6ae02143f46d13d2f09f4562644c00f1",
3773
+ "bytes": 2419197,
3774
+ "contentType": "video/mp4"
3775
+ },
3776
+ "poster": {
3777
+ "objectKey": "authored-motion/1.0.0/night-sales/previews/e6b0de6230215bffae1567b6b3a3caf9e4e3f4c92918e91af62f1ba108c9e44d.jpg",
3778
+ "sha256": "e6b0de6230215bffae1567b6b3a3caf9e4e3f4c92918e91af62f1ba108c9e44d",
3779
+ "bytes": 91168,
3780
+ "contentType": "image/jpeg"
3781
+ }
3782
+ },
3783
+ {
3784
+ "ratio": "tall",
3785
+ "width": 1080,
3786
+ "height": 1920,
3787
+ "duration": 12,
3788
+ "video": {
3789
+ "objectKey": "authored-motion/1.0.0/night-sales/previews/2b60a88258aa1274b9cb303bcab8d92935faba996c6077dcde69f30746def2d1.mp4",
3790
+ "sha256": "2b60a88258aa1274b9cb303bcab8d92935faba996c6077dcde69f30746def2d1",
3791
+ "bytes": 4138411,
3792
+ "contentType": "video/mp4"
3793
+ },
3794
+ "poster": {
3795
+ "objectKey": "authored-motion/1.0.0/night-sales/previews/a31c588c91e8d7f4ad7b0a95988fddccd23090daee3760b0a67cc9be221d9581.jpg",
3796
+ "sha256": "a31c588c91e8d7f4ad7b0a95988fddccd23090daee3760b0a67cc9be221d9581",
3797
+ "bytes": 151623,
3798
+ "contentType": "image/jpeg"
3799
+ }
3800
+ }
3801
+ ],
3802
+ "qa": {
3803
+ "status": "source-and-preview-hashes-verified",
3804
+ "previewCount": 3,
3805
+ "artisticApproval": "not-certified",
3806
+ "audioListened": false,
3807
+ "transferCertified": false
3808
+ },
3809
+ "restrictions": [
3810
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
3811
+ "Scene-specific data/count/layout limits are documented in the selected card.",
3812
+ "Opaque output only; no alpha export.",
3813
+ "Voice/music requires timeScale=1 until explicitly retimed.",
3814
+ "Inspect frame boundaries, typography and final audio before delivery."
3815
+ ]
3816
+ },
3817
+ {
3818
+ "id": "cv-scan",
3819
+ "version": "1.0.0",
3820
+ "title": "7 segundos",
3821
+ "mechanism": "gaze-path-reorganize",
3822
+ "intent": "Jerarqu\xEDa de informaci\xF3n: dos lecturas medidas igual, antes y despu\xE9s de reorganizar el documento.",
3823
+ "niche": "empleo",
3824
+ "style": "UI",
3825
+ "duration": 12,
3826
+ "runtime": "canvas",
3827
+ "supportedRatios": [
3828
+ "16:9",
3829
+ "1:1",
3830
+ "9:16"
3831
+ ],
3832
+ "sourceSha256": "232efe3a0b8dce5198c91e4fe7e1f8e781311fa86a0821ee9df4b466dc14c7d0",
3833
+ "cardSha256": "064b7207ff3fcc241a7adfb86fefe88ca1e3bff2e5b88bd5c3d418fa8c591914",
3834
+ "card": '# cv-scan \xB7 7 segundos para leer tu CV\n\n**Mecanismo.** Una mirada simulada (fijaciones cuyo tama\xF1o crece con la permanencia + l\xEDneas de s\xE1cada) lee un CV saturado bajo un dial de `limit` segundos, se pierde y toca solo 2 de 8 datos clave; los no tocados se encierran a mano en rojo. Una l\xEDnea de escaneo "Jerarqu\xEDa" barre la hoja: arriba de ella ya est\xE1 el CV limpio y cada dato salta de su lugar viejo al nuevo en cuanto la l\xEDnea lo alcanza. Una segunda lectura, corta y directa, toca los 8. Los conteos son hit-tests de las fijaciones contra las cajas de los datos; los tiempos son la suma de permanencias + s\xE1cadas. La lectura 1 se escala a exactamente `limit` y ambas comparten la misma velocidad real, as\xED que la 2.\xAA, m\xE1s corta, es honesta. Protagonista: la mirada (perdida \u2192 borrada por el escaneo \u2192 l\xEDnea recta).\n\n**\xDAsala para.** Coaches de carrera, bolsas de trabajo, reclutadores, cursos de CV o LinkedIn, servicios de redacci\xF3n de CV. **No la uses para.** Otros documentos (la estructura nueva es de CV y est\xE1 fija en c\xF3digo); presentar datos reales de eye-tracking (la mirada es ilustrativa); CVs con m\xE1s de 3 logros o skills en varias filas.\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| page | 0 | La hoja sube (0.6 s); HUD 0.25\u20130.75: "LECTURA 1", dial vac\xEDo, `deadline`, contador 0/8. |\n| g1 | 0.75 | Lectura 1: 12 fijaciones en ~3.6 s reales (= `limit` simulados); el dial avanza; cada acierto enmarca el dato en `pos` y sube el contador. |\n| miss | 4.5 | La lectura acaba ~0.15 s antes; dial y conteo en `neg`; los datos no tocados se encierran a mano en `neg`, uno cada 0.08 s. |\n| scan | 5.2 | L\xEDnea de 1.8 s a velocidad casi constante; abajo queda el CV viejo, arriba el nuevo; cada dato salta en 0.45 s al pasar la l\xEDnea por su lugar viejo (si el nuevo est\xE1 m\xE1s abajo, viaja pegado a la l\xEDnea). |\n| g2 | 7.45 | Desde \u22120.75 el "2/8" baja a la fila de historial "Lectura 1 2/8 \xB7 7.0 s" y el contador reinicia; lectura 2 (8 fijaciones, ~1.66 s); subrayado `pos` por acierto; 8/8 y dial en `pos`. |\n| result | 9.35 | La mirada 2 se aten\xFAa 70 % (+0.3\u2026+1.0). |\n| end | 12 | Hold con ambos puntajes (~2.6 s). |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand, duration, fps, timeScale | kit `office`, 12 s | \u2014 |\n| limit | segundos de la lectura 1 (escala sus permanencias) y escala del dial | `copy.deadline` NO se recalcula: c\xE1mbialo a mano |\n| facts[] | `{id, text, old: [l\xEDnea, x 0\u20131], style}`; style \u2208 `name`, `role`, `meta`, `achievement`, `chips` | 1 `name`, 1 `role`; `meta` en una fila sin wrap; `achievement` se parte en el primer espacio (n\xFAmero grande + resto), m\xE1x. 3 (el 4.\xBA choca con "Habilidades"); `chips` se parte por " \xB7 ", una fila sin wrap; dato que no cabe en su l\xEDnea vieja \u2192 warning |\n| oldPage | `lines`, `blank`, `heads` {l\xEDnea: t\xEDtulo}, `words` (relleno) | el resto son barras grises sembradas |\n| gaze1 | `[x, y, permanencia]` en fracciones de hoja | dise\xF1ada para tocar 2 datos; si no, warning `gaze 1 hits n facts`; recalibrar si mueves `facts.old` |\n| gaze2 | `[factId, permanencia]`; posici\xF3n sobre la caja nueva (jitter sembrado) | su suma + s\xE1cadas = tiempo mostrado; debe terminar antes de `result` |\n| saccade | segundos simulados por salto (0.05) | \u2014 |\n| copy.read | `{n}` = 1 o 2 | \u2014 |\n| copy.seen / scan / unit / note / deadline | r\xF3tulo del contador / pastilla de la l\xEDnea / unidad del dial / aviso / leyenda bajo el dial | `deadline` sin maxW |\n| copy.sections | 4 r\xF3tulos del CV nuevo | exactamente 4, posiciones fijas |\n| beats | ver tabla | velocidad real = (`miss` \u2212 0.15 \u2212 `g1`) / `limit` |\n| sound.perFixation / perHit / perLand | tick por fijaci\xF3n, pop por acierto, snap por dato que aterriza (se generan en setup) | si existe `sound`, las tres claves deben existir |\n| sound.cues | 9 cues | \u2014 |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- El CV (nombre, puesto, a\xF1os, idioma, logros, skills), su lugar en la hoja vieja, ambos recorridos, `limit` y textos.\n- Beats con la voz: `g1` en "un reclutador lo lee", `scan` en "con jerarqu\xEDa", `g2` en "en cambio".\n- Roles: `surface` (hoja), `accent` (puesto, r\xF3tulos de secci\xF3n, l\xEDnea), `accent2` (mirada), `pos` (aciertos, 8/8), `neg` (fallos, tiempo agotado), `line`, `muted`, `ink`. Fuentes: `text`, `heavy` (cifras), `display` (leyenda), `mono`.\n\n## Qu\xE9 requiere tocar c\xF3digo\n- Estructura del CV nuevo (`newY`: posici\xF3n fija por estilo; barras de contacto, experiencia y educaci\xF3n).\n- M\xE1s logros, skills en dos filas, foto, otro tipo de documento.\n\n## Formatos\n- 16:9: HUD a la izquierda (430u) + hoja (1000u de alto) centrados como grupo; contador 140u.\n- 1:1: hoja a la izquierda (x 40u) y HUD en la franja derecha (~297 px); contador 104u; leyenda de 27u sin maxW: textos largos se salen por la derecha.\n- 9:16: HUD arriba (dial a la izquierda, contador a la derecha), hoja de 960u de ancho desde y = 500u (texto viejo \u2248 18 px).\n\n## Sonido\nblip al iniciar cada lectura; ticks por fijaci\xF3n, pops por acierto y snaps por aterrizaje (generados); drop + 2 scribbles en `miss`; whoosh + paper en `scan`; success + swell en `result`. Funciona muda.\n\n## Limitaciones\n- Mirada simulada e ilustrativa; persona y datos ficticios (`copy.note`).\n- El CV viejo es ilegible a prop\xF3sito (9.5 cu); el nuevo solo dibuja los 8 datos y barras de relleno.\n- `gaze1` es a mano: si cambias el texto o el lugar de un dato, revisa el warning de aciertos.\n\n## Adaptar a otro tema\n- Backend (`theme: indigo`): `facts` nombre "Andr\xE9s Leal", role "Ingeniero Backend", meta "6 a\xF1os de experiencia" e "Ingl\xE9s B2", achievements "\u221240% latencia en APIs", "3 servicios migrados a Go", "99.9% de uptime", chips "Go \xB7 PostgreSQL \xB7 AWS" (mismos `id` para que `gaze2` siga v\xE1lido).\n- Ventas B2B (`theme: office`): role "Ejecutiva de Ventas B2B", achievements "+120% de cuota en 2024", "$4M en contratos nuevos", "35 cuentas activas", chips "Salesforce \xB7 HubSpot \xB7 Negociaci\xF3n".\n- Reclutador m\xE1s r\xE1pido: `limit 6`, `copy.deadline "6 s para decidir"`.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Sin media (no hay `params.media`): hoja, mirada y HUD se dibujan con c\xF3digo.\n',
3835
+ "bundle": {
3836
+ "objectKey": "authored-motion/1.0.0/cv-scan/9e8e41aaa6c9b3bee42998a68e319d9de1d1ff8ba22a1cbbc94edce3f9f04365.tar.gz",
3837
+ "sha256": "9e8e41aaa6c9b3bee42998a68e319d9de1d1ff8ba22a1cbbc94edce3f9f04365",
3838
+ "bytes": 2941312,
3839
+ "fileCount": 58,
3840
+ "contentType": "application/gzip"
3841
+ },
3842
+ "previews": [
3843
+ {
3844
+ "ratio": "wide",
3845
+ "width": 1920,
3846
+ "height": 1080,
3847
+ "duration": 12,
3848
+ "video": {
3849
+ "objectKey": "authored-motion/1.0.0/cv-scan/previews/38e5958b60191cce6eb12b618abac06af23392ba880b369ff03c4bfec0d4ca12.mp4",
3850
+ "sha256": "38e5958b60191cce6eb12b618abac06af23392ba880b369ff03c4bfec0d4ca12",
3851
+ "bytes": 1342525,
3852
+ "contentType": "video/mp4"
3853
+ },
3854
+ "poster": {
3855
+ "objectKey": "authored-motion/1.0.0/cv-scan/previews/837de97da00e48bbe92b2859efdfe705a71912cfad141b1a92843d75d45223b7.jpg",
3856
+ "sha256": "837de97da00e48bbe92b2859efdfe705a71912cfad141b1a92843d75d45223b7",
3857
+ "bytes": 110961,
3858
+ "contentType": "image/jpeg"
3859
+ }
3860
+ },
3861
+ {
3862
+ "ratio": "square",
3863
+ "width": 1080,
3864
+ "height": 1080,
3865
+ "duration": 12,
3866
+ "video": {
3867
+ "objectKey": "authored-motion/1.0.0/cv-scan/previews/05622f5da7042080339730ea5a5b38b8c0200518c5720071b5b1dcc0bc4c18e2.mp4",
3868
+ "sha256": "05622f5da7042080339730ea5a5b38b8c0200518c5720071b5b1dcc0bc4c18e2",
3869
+ "bytes": 1258346,
3870
+ "contentType": "video/mp4"
3871
+ },
3872
+ "poster": {
3873
+ "objectKey": "authored-motion/1.0.0/cv-scan/previews/0b86bb7f0620989661513a9d3635fd72b4e4c22dcdb28897fa7f860ec373dc39.jpg",
3874
+ "sha256": "0b86bb7f0620989661513a9d3635fd72b4e4c22dcdb28897fa7f860ec373dc39",
3875
+ "bytes": 96152,
3876
+ "contentType": "image/jpeg"
3877
+ }
3878
+ },
3879
+ {
3880
+ "ratio": "tall",
3881
+ "width": 1080,
3882
+ "height": 1920,
3883
+ "duration": 12,
3884
+ "video": {
3885
+ "objectKey": "authored-motion/1.0.0/cv-scan/previews/aed90f5e444a667842b504acc12f322a79a721cf3ef2ae20d51cd01a76596069.mp4",
3886
+ "sha256": "aed90f5e444a667842b504acc12f322a79a721cf3ef2ae20d51cd01a76596069",
3887
+ "bytes": 1736956,
3888
+ "contentType": "video/mp4"
3889
+ },
3890
+ "poster": {
3891
+ "objectKey": "authored-motion/1.0.0/cv-scan/previews/82491dbc71fd1a4fab242b87eeb9338b5072a14f44231380c1006d4f58258620.jpg",
3892
+ "sha256": "82491dbc71fd1a4fab242b87eeb9338b5072a14f44231380c1006d4f58258620",
3893
+ "bytes": 145181,
3894
+ "contentType": "image/jpeg"
3895
+ }
3896
+ }
3897
+ ],
3898
+ "qa": {
3899
+ "status": "source-and-preview-hashes-verified",
3900
+ "previewCount": 3,
3901
+ "artisticApproval": "not-certified",
3902
+ "audioListened": false,
3903
+ "transferCertified": false
3904
+ },
3905
+ "restrictions": [
3906
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
3907
+ "Scene-specific data/count/layout limits are documented in the selected card.",
3908
+ "Opaque output only; no alpha export.",
3909
+ "Voice/music requires timeScale=1 until explicitly retimed.",
3910
+ "Inspect frame boundaries, typography and final audio before delivery."
3911
+ ]
3912
+ },
3913
+ {
3914
+ "id": "sankey-money",
3915
+ "version": "1.0.0",
3916
+ "title": "De cada $100",
3917
+ "mechanism": "sankey-split",
3918
+ "intent": "Transparencia de dinero: un total que se divide en flujos proporcionales y resalta el que importa.",
3919
+ "niche": "ONG",
3920
+ "style": "datos",
3921
+ "duration": 12,
3922
+ "runtime": "canvas",
3923
+ "supportedRatios": [
3924
+ "16:9",
3925
+ "1:1",
3926
+ "9:16"
3927
+ ],
3928
+ "sourceSha256": "cf8158434f7cd7af68da78b4b424ca2ffc1c04bbce2d52e4a7d834230d67e6a9",
3929
+ "cardSha256": "56a462792a16c2a152d4b8554ea9d09e20a0c73fc370e836fcf11f985e01b7e0",
3930
+ "card": '# sankey-money \xB7 A d\xF3nde va cada peso\n\n**Mecanismo.** Una banda de `$100` entra desde la izquierda y se divide, de izquierda a derecha, en cintas Sankey: primero en los `flows`, luego el flujo foco en sus `children`. El grosor es aritm\xE9tica exacta: cada cinta mide `valor \xD7 k` px en ambos extremos (los dos bordes son la misma c\xFAbica desplazada). Un wipe izquierda\u2192derecha pinta la rama foco en `accent2` y casi borra el resto; un pulso recorre cada cinta hija en orden (su valor rebota al llegar) y un callout "82\xA2 de cada peso" se ancla con un pin. Porcentaje, montos y total se calculan de los datos.\n\n**\xDAsala para.** ONG o fundaci\xF3n (transparencia de donativos), presupuesto p\xFAblico, finanzas personales (a d\xF3nde va tu sueldo), restaurante (de cada $100 de la cuenta), SaaS (desglose del precio). **No la uses para.** M\xE1s de 2 niveles o m\xE1s de un flujo con hijos, datos que no sumen ~100, montos grandes (la altura es valor \xD7 k), comparar periodos, flujos que se juntan.\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| enter | 0.3 | La banda entra desde el borde (fade en el lado izquierdo) y llega a la columna 0 en `node`. |\n| node | 1.25 | Regla del nodo fuente; "$100 que donas" aparece sobre la banda (0.45 s). |\n| split1 | 1.45 | Las cintas crecen de la columna 0 a la 1 en `split2 \u2212 split1 \u2212 0.45` s (1.85); al llegar: nodos, etiquetas de los flujos no foco (el valor cuenta desde 0) y monto + nombre del foco dentro de su cinta. |\n| split2 | 3.75 | Las hijas crecen a la columna 2 en 1.65 s; etiquetas escalonadas 0.08 s. |\n| focus | 6.2 | Wipe de 1.1 s: foco e hijas a `accent2` (mezcla 82 %), el resto se apaga; etiquetas no foco a `muted`; la del foco cambia de color justo en el borde del wipe. |\n| pulse | 7.45 | Un pulso por hija cada 0.45 s (0.75 s cada uno); el valor rebota 14 % al llegar. |\n| callout | 9.0 | Pin + l\xEDnea + `{pct}` grande + sufijo + `calloutLines`. |\n| end | 12 | No lo usa el c\xF3digo; ~2.6 s de hold. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand, duration, fps, timeScale | kit `press`, 12 s | usa `bg`, `ink`, `muted`, `accent2` (no `accent` ni `series`) |\n| currency | prefijo; montos enteros sin separador de miles | \u2014 |\n| per | solo valida: warning `flows suman X, no per` | la fuente muestra la suma real, no `per` |\n| focus | `id` del flujo que se abre en hijos | si no existe usa el primero; el foco debe tener `children` o la escena falla. Ponlo **primero** en `flows`: el pin del callout se ancla al borde superior de la banda (entre columnas 0 y 1), no a la rama foco |\n| flows[] | `{id, label, value, children?}`; `children` de flujos no foco se ignoran | suma \u2248 100 (altura = valor \xD7 5u en 16:9, 4.5u en 1:1, 9.6u en 9:16); el foco necesita valor \u2265 ~20 para que su etiqueta de 2 l\xEDneas quepa en la cinta; dos hijas vecinas que sumen < ~9 enciman sus etiquetas (16:9/1:1); el hueco del valor es de 2 cifras; warning si los hijos no suman al padre |\n| copy.source | texto junto al monto fuente | \u2014 |\n| copy.callout | se dibuja SIEMPRE el % grande y luego el texto con `{n}` borrado, al 62 % | solo sufijos: "{n}\xA2", "{n} %" |\n| copy.calloutLines | l\xEDneas apiladas a la derecha del n\xFAmero | maxW solo avisa |\n| copy.note | aviso abajo-izquierda | \u2014 |\n| beats | ver tabla | `split2 \u2212 split1` > 0.45 |\n| sound.cues | 12; los `tick` a split1 +1.85 y split2 +1.65 marcan las llegadas | si mueves split1/split2: offset del primero = split2 \u2212 split1 \u2212 0.45 |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- Flujos, hijos, foco, moneda, textos del callout; el % y los montos se recalculan.\n- Beats con la voz: `split1` en "de cada peso\u2026", `focus` en el n\xFAmero clave, `callout` en la conclusi\xF3n.\n- Roles: `accent2` (rama foco, pulsos, callout), `ink` (nodos, etiquetas, fuente), `muted` (etiquetas apagadas, nota), `bg` (base de las mezclas de color y bordes de las cintas). Fuentes: `heavy` (cifras tabulares), `display` (nombres, callout), `text` (nota).\n\n## Qu\xE9 requiere tocar c\xF3digo\n- Tercer nivel, varios flujos con hijos o flujos que convergen.\n- Decimales o separador de miles; color por categor\xEDa (`series` no se usa); etiquetas a la izquierda.\n- Anclar el pin del callout a una rama foco que no sea la primera.\n\n## Formatos\n- 16:9: columnas en x = 250 / 880 / 1430u, k = 5u, banda desde y 370u; fuente en una l\xEDnea; callout 132u, 120u sobre la banda.\n- 1:1: x = 150 / 500 / 780u, k = 4.5u; fuente apilada (monto sobre texto); callout 112u.\n- 9:16: x = 120 / 430 / 700u, k = 9.6u (banda de 960 px desde y 640u); fuente apilada; callout 190u; las etiquetas de la columna 2 tienen ~200 px antes del borde.\n\n## Sonido\nwhoosh-short en `enter`; snap en `node`; pour + tick (llegada) en cada split; whoosh en `focus`; 3 blips en `pulse` (+0.5 / +0.95 / +1.4, cuando cada pulso llega); coin (+0.3) y swell (+0.8) en `callout`. Funciona muda.\n\n## Limitaciones\n- Cifras ilustrativas y organizaci\xF3n ficticia (`copy.note`).\n- Tama\xF1o ligado a los datos: con suma \u2260 ~100 la figura se sale o encoge (no hay autoescala).\n- Las etiquetas no se reacomodan: hijas peque\xF1as y vecinas se enciman (la separaci\xF3n entre hijas es solo 18u en 16:9).\n\n## Adaptar a otro tema\n- Sueldo (`theme: ledger`): `source "de tu sueldo"`, `focus "fijos"`, `flows [{id:"fijos",label:"gastos fijos",value:55,children:[{label:"renta",value:30},{label:"comida",value:15},{label:"transporte",value:10}]},{id:"ahorro",label:"ahorro",value:20},{id:"ocio",label:"ocio",value:25}]`, `calloutLines ["de cada peso","se va en gastos fijos"]`.\n- Restaurante (`theme: trattoria`): `source "que pagas"`, `focus "costos"`, `flows [{id:"costos",label:"costos",value:64,children:[{label:"insumos",value:34},{label:"n\xF3mina",value:30}]},{id:"renta",label:"renta",value:14},{id:"utilidad",label:"utilidad",value:22}]`, `calloutLines ["de cada peso","cubre insumos y n\xF3mina"]`.\n- Presupuesto municipal (`theme: press`): `source "de presupuesto"`, `focus "obra"`, `flows [{id:"obra",label:"obra p\xFAblica",value:48,children:[{label:"agua",value:20},{label:"calles",value:18},{label:"parques",value:10}]},{id:"nomina",label:"n\xF3mina",value:38},{id:"deuda",label:"deuda",value:14}]`, `calloutLines ["de cada peso","va a obra p\xFAblica"]`.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Sin media (no hay `params.media`).\n',
3931
+ "bundle": {
3932
+ "objectKey": "authored-motion/1.0.0/sankey-money/daf9b316b04dcc800aa53b07ff6fc593a3f1fc8aea98f038ea39108541eda353.tar.gz",
3933
+ "sha256": "daf9b316b04dcc800aa53b07ff6fc593a3f1fc8aea98f038ea39108541eda353",
3934
+ "bytes": 2936404,
3935
+ "fileCount": 58,
3936
+ "contentType": "application/gzip"
3937
+ },
3938
+ "previews": [
3939
+ {
3940
+ "ratio": "wide",
3941
+ "width": 1920,
3942
+ "height": 1080,
3943
+ "duration": 12,
3944
+ "video": {
3945
+ "objectKey": "authored-motion/1.0.0/sankey-money/previews/f23b0f519f35fe00f22ed6ff0b2b7e78c8e73ea3f3dda09082a7dba16d3f869d.mp4",
3946
+ "sha256": "f23b0f519f35fe00f22ed6ff0b2b7e78c8e73ea3f3dda09082a7dba16d3f869d",
3947
+ "bytes": 489135,
3948
+ "contentType": "video/mp4"
3949
+ },
3950
+ "poster": {
3951
+ "objectKey": "authored-motion/1.0.0/sankey-money/previews/68d92316fbd57a6e2548a320cbfff6cc952f2bb5a7d56e4397e67bd58aca9adf.jpg",
3952
+ "sha256": "68d92316fbd57a6e2548a320cbfff6cc952f2bb5a7d56e4397e67bd58aca9adf",
3953
+ "bytes": 83665,
3954
+ "contentType": "image/jpeg"
3955
+ }
3956
+ },
3957
+ {
3958
+ "ratio": "square",
3959
+ "width": 1080,
3960
+ "height": 1080,
3961
+ "duration": 12,
3962
+ "video": {
3963
+ "objectKey": "authored-motion/1.0.0/sankey-money/previews/43c739be2b035d308cbd156f3bf82f8490449a7f85ef543c454c831ef18916fe.mp4",
3964
+ "sha256": "43c739be2b035d308cbd156f3bf82f8490449a7f85ef543c454c831ef18916fe",
3965
+ "bytes": 427794,
3966
+ "contentType": "video/mp4"
3967
+ },
3968
+ "poster": {
3969
+ "objectKey": "authored-motion/1.0.0/sankey-money/previews/ed09f233055b7a2b9acac4c0e552d6000311484125525e3c012fe56c539d46f5.jpg",
3970
+ "sha256": "ed09f233055b7a2b9acac4c0e552d6000311484125525e3c012fe56c539d46f5",
3971
+ "bytes": 59001,
3972
+ "contentType": "image/jpeg"
3973
+ }
3974
+ },
3975
+ {
3976
+ "ratio": "tall",
3977
+ "width": 1080,
3978
+ "height": 1920,
3979
+ "duration": 12,
3980
+ "video": {
3981
+ "objectKey": "authored-motion/1.0.0/sankey-money/previews/154ebb8dc62eb10e963a7a2cad65c31610d6a6b9670a5a6a6b83d4074756e78a.mp4",
3982
+ "sha256": "154ebb8dc62eb10e963a7a2cad65c31610d6a6b9670a5a6a6b83d4074756e78a",
3983
+ "bytes": 476600,
3984
+ "contentType": "video/mp4"
3985
+ },
3986
+ "poster": {
3987
+ "objectKey": "authored-motion/1.0.0/sankey-money/previews/f72a067d48ea4ef2250f8a5621501e09ff717e58e7fa50099dc191b246724e48.jpg",
3988
+ "sha256": "f72a067d48ea4ef2250f8a5621501e09ff717e58e7fa50099dc191b246724e48",
3989
+ "bytes": 84643,
3990
+ "contentType": "image/jpeg"
3991
+ }
3992
+ }
3993
+ ],
3994
+ "qa": {
3995
+ "status": "source-and-preview-hashes-verified",
3996
+ "previewCount": 3,
3997
+ "artisticApproval": "not-certified",
3998
+ "audioListened": false,
3999
+ "transferCertified": false
4000
+ },
4001
+ "restrictions": [
4002
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
4003
+ "Scene-specific data/count/layout limits are documented in the selected card.",
4004
+ "Opaque output only; no alpha export.",
4005
+ "Voice/music requires timeScale=1 until explicitly retimed.",
4006
+ "Inspect frame boundaries, typography and final audio before delivery."
4007
+ ]
4008
+ },
4009
+ {
4010
+ "id": "bar-race",
4011
+ "version": "1.0.0",
4012
+ "title": "La tabla de goleo",
4013
+ "mechanism": "bar-chart-race-to-card",
4014
+ "intent": "Rankings en el tiempo con un protagonista que escala; termina en ficha de estad\xEDsticas.",
4015
+ "niche": "deportes",
4016
+ "style": "datos",
4017
+ "duration": 14,
4018
+ "runtime": "canvas",
4019
+ "supportedRatios": [
4020
+ "16:9",
4021
+ "1:1",
4022
+ "9:16"
4023
+ ],
4024
+ "sourceSha256": "6d65cedd741de2683e2049b6f7385d12462af33d9292ed418c7332293ccdb0c3",
4025
+ "cardSha256": "ab6a2a1be1dd30610210d74cc506458c47d98dc8b568a8b1664fd68e6e4f252b",
4026
+ "card": '# bar-race \xB7 La remontada en la tabla\n\n**Mecanismo.** Bar chart race con look de transmisi\xF3n deportiva (todo inclinado 0.2). De los goles por jornada salen acumulados y un rango entero por jornada (empates: se conserva el orden anterior); rango y valor se interpolan con c\xFAbica mon\xF3tona (sin overshoot), as\xED que los rebases se leen como deslizamientos y no como saltos. Los valores cuentan, el eje se reescala y un od\xF3metro de jornada rueda con sus ticks. La fila destacada (`accent`) sube lugar por lugar: su casilla de rango se enciende al llegar, una cu\xF1a verde la acompa\xF1a mientras sube y un brillo la barre al tomar el #1 y al final. Luego las dem\xE1s salen, su barra se estira y se vuelve tarjeta con rango, nombre y 3 cifras calculadas (goles = suma, asistencias = dato, min/gol = `minutes / goles`).\n\n**\xDAsala para.** Deportes (goleo, triples, asistencias), ranking de vendedores por semana, sucursales por rese\xF1as al mes, creadores por publicaciones, productos m\xE1s vendidos; siempre con un protagonista que remonta. **No la uses para.** M\xE1s de 8 filas (9 en 9:16), totales grandes (la rejilla va cada 1/2/5 unidades: arriba de ~60 se satura), decimales, valores que bajan.\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| intro | 0 | Lower third (bug + t\xEDtulo) con wipe diagonal (0\u20130.7); filas entran por rango inicial (0.2 + 0.05\xB7rango); contador 0.3\u20130.9; eje 0.4\u20131.0. |\n| race | 1.3 | Jornada 1 \u2192 `jornadas` en `raceEnd \u2212 race` s (0.5 s por jornada con 17); el od\xF3metro rueda en el primer 35 % de cada intervalo; la \xFAltima jornada jugada se marca en `accent` en los ticks. |\n| (calculados) | \u2014 | Cada rebase del destacado = whoosh-short; si llega a #1: riser 1.2 s antes, success y barrido de brillo. |\n| raceEnd | 9.3 | Contador fijo en la \xFAltima jornada; segundo barrido de brillo. |\n| out | 9.8 | Las dem\xE1s filas y casillas salen 260u a la izquierda, escalonadas 0.03 s; el eje se va. |\n| extend | 10.3 | La barra destacada se estira al borde de la tarjeta (0.4 s); su valor se apaga. |\n| card | 10.7 | La barra se vuelve tarjeta (0.6 s); el contador baja a 45 %; desde +0.4 entran con wipe "#r DE LA TABLA", nombre completo (fit), `sub` y divisor. |\n| stats | 11.25 | 3 cifras cuentan, escalonadas 0.18 s. |\n| end | 14 | No lo usa el c\xF3digo; ~2.4 s de hold. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand, duration, fps, timeScale | kit `broadcast`, 14 s | inclinaci\xF3n 0.2, sombras negras y brillos blancos fijos |\n| jornadas | n\xFAmero de periodos: ticks y od\xF3metro | cada `goals` debe tener esa longitud (warning; con menos, la escena se rompe); od\xF3metro de 2 d\xEDgitos (< 100) |\n| highlight | `id` del protagonista | si no existe, usa el primero |\n| players[] | `{id, name, club, goals[], position?, assists?, minutes?}` | nombre de fila = inicial + apellidos en MAY\xDAS, sin maxW (apellidos largos invaden la barra); `club` con maxW; el destacado necesita `assists` y `minutes` (si faltan: "NaN") |\n| copy.bug / title / league | lower third (bug y t\xEDtulo con maxW que solo avisa) | \u2014 |\n| copy.jornada / axis | r\xF3tulo del contador / del eje | \u2014 |\n| copy.rankTag | `{r}` = rango final del destacado | \u2014 |\n| copy.sub | `{position}` `{club}` `{n}` (= jornadas), en MAY\xDAS | sin `position` empieza con " \xB7 " |\n| copy.stats | 3 r\xF3tulos | exactamente 3; f\xF3rmulas fijas |\n| copy.note | aviso abajo-izquierda | \u2014 |\n| beats | ver tabla | `race`\u2192`raceEnd` fija la velocidad |\n| sound | `cues` vac\xEDo: todo sale de `cues(S)` | \u2014 |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- Jugadores, datos por jornada, protagonista, textos; rebases, momento del #1, tarjeta y sonidos se recalculan.\n- Beats con la voz: `race` en "jornada 1", `raceEnd` en "al final", `card` en el nombre.\n- Roles: `accent` (fila destacada, casilla encendida, bug, tarjeta), `accent2` (raya del lower third), `pos` (cu\xF1a de subida), `surface`/`line`/`muted` (placas y barras grises), `ink`, `bg`. Fuentes: `heavy` (nombres, cifras), `text`, `mono` (eje).\n\n## Qu\xE9 requiere tocar c\xF3digo\n- Otras cifras o f\xF3rmulas en la tarjeta; m\xE1s de 3.\n- M\xE1s filas (posiciones `y0`/`rp` por formato), escudos o fotos, decimales, otro paso de rejilla.\n- Quitar la inclinaci\xF3n (`SK`).\n\n## Formatos\n- 16:9: barras de 560 a 1210u, filas cada 84u desde y 226u; contador gigante (380u) a la derecha con ticks debajo; lower third abajo-izquierda (y 922u).\n- 1:1: barras 430\u2013930u, filas cada 68u desde 382u; contador chico (180u) arriba-izquierda con ticks al lado.\n- 9:16: contador 400u arriba-izquierda con ticks debajo; filas cada 100u desde 790u; tarjeta 742\u20131520u con las cifras apiladas.\n\n## Sonido\nTodo generado: whoosh + whoosh-short al inicio; tick por cambio de jornada; whoosh-short por rebase del destacado (m\xE1s fuerte al #1); riser + success si llega a #1 antes de `raceEnd`; whoosh-short en `out`, whoosh en `extend`; snap al formarse la tarjeta; 3 blips + stamp en `stats`. Funciona muda.\n\n## Limitaciones\n- Liga, jugadores, clubes y cifras ficticios (`copy.note`).\n- La tarjeta asume que el destacado termina #1: si no, la casilla "1" se queda hasta `card` y la tarjeta igual sale.\n- Con muchas jornadas (> ~30) los ticks se vuelven muy delgados y los tick SFX muy seguidos.\n\n## Adaptar a otro tema\n- Vendedores (`theme: office`): `jornadas 12`, `copy.jornada "SEMANA"`, `axis "CIERRES"`, `bug "VENTAS"`, `title "As\xED se movi\xF3 el ranking del trimestre"`, `goals` = cierres por semana (0\u20133), destacado con `assists` = referidos y `minutes` = minutos en llamada, `stats ["CIERRES","REFERIDOS","MIN / CIERRE"]`.\n- B\xE1squet amateur (`theme: sport`): `goals` = triples por partido, `jornadas 14`, `copy.jornada "PARTIDO"`, `axis "TRIPLES"`, `sub "{position} \xB7 {club} \xB7 {n} partidos"`.\n- Sucursales (`theme: transit`): `goals` = rese\xF1as 5\u2605 por mes (\u2264 5), `jornadas 12`, `copy.jornada "MES"`, `name` = "Sucursal Centro", `club` = ciudad abreviada; `stats` siguen siendo 3 f\xF3rmulas fijas.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Sin media (no hay `params.media`).\n',
4027
+ "bundle": {
4028
+ "objectKey": "authored-motion/1.0.0/bar-race/adc4ffa31299daef78e0cf99ee40f8b08400fbdb53a9b6637e01625e3a191857.tar.gz",
4029
+ "sha256": "adc4ffa31299daef78e0cf99ee40f8b08400fbdb53a9b6637e01625e3a191857",
4030
+ "bytes": 2940761,
4031
+ "fileCount": 58,
4032
+ "contentType": "application/gzip"
4033
+ },
4034
+ "previews": [
4035
+ {
4036
+ "ratio": "wide",
4037
+ "width": 1920,
4038
+ "height": 1080,
4039
+ "duration": 14,
4040
+ "video": {
4041
+ "objectKey": "authored-motion/1.0.0/bar-race/previews/7ed9c5087ce0766cbefdcb65c6001d04cc773c4717dffe4471de66d0189da492.mp4",
4042
+ "sha256": "7ed9c5087ce0766cbefdcb65c6001d04cc773c4717dffe4471de66d0189da492",
4043
+ "bytes": 2156422,
4044
+ "contentType": "video/mp4"
4045
+ },
4046
+ "poster": {
4047
+ "objectKey": "authored-motion/1.0.0/bar-race/previews/a533f384a95bae52a53e46b7a8344ae88c886d0018280e95ee62747b3461756d.jpg",
4048
+ "sha256": "a533f384a95bae52a53e46b7a8344ae88c886d0018280e95ee62747b3461756d",
4049
+ "bytes": 166071,
4050
+ "contentType": "image/jpeg"
4051
+ }
4052
+ },
4053
+ {
4054
+ "ratio": "square",
4055
+ "width": 1080,
4056
+ "height": 1080,
4057
+ "duration": 14,
4058
+ "video": {
4059
+ "objectKey": "authored-motion/1.0.0/bar-race/previews/34b582b9538b5f53ecaca8c538c3879a7c4b25d106b55a5908cbe46309bcbeba.mp4",
4060
+ "sha256": "34b582b9538b5f53ecaca8c538c3879a7c4b25d106b55a5908cbe46309bcbeba",
4061
+ "bytes": 1611508,
4062
+ "contentType": "video/mp4"
4063
+ },
4064
+ "poster": {
4065
+ "objectKey": "authored-motion/1.0.0/bar-race/previews/de8abc95a71a521496471a10661af49d21fbc1cc4c2c0beb20b463224bf299fc.jpg",
4066
+ "sha256": "de8abc95a71a521496471a10661af49d21fbc1cc4c2c0beb20b463224bf299fc",
4067
+ "bytes": 115055,
4068
+ "contentType": "image/jpeg"
4069
+ }
4070
+ },
4071
+ {
4072
+ "ratio": "tall",
4073
+ "width": 1080,
4074
+ "height": 1920,
4075
+ "duration": 14,
4076
+ "video": {
4077
+ "objectKey": "authored-motion/1.0.0/bar-race/previews/25bda6d28992c003488f194fa37f011b9221c131007b5d42770112aa8df90ab7.mp4",
4078
+ "sha256": "25bda6d28992c003488f194fa37f011b9221c131007b5d42770112aa8df90ab7",
4079
+ "bytes": 2139594,
4080
+ "contentType": "video/mp4"
4081
+ },
4082
+ "poster": {
4083
+ "objectKey": "authored-motion/1.0.0/bar-race/previews/2046d2290f74a098bd0f3d822c8111d8e2f16af72738130f7513325952ee5b3a.jpg",
4084
+ "sha256": "2046d2290f74a098bd0f3d822c8111d8e2f16af72738130f7513325952ee5b3a",
4085
+ "bytes": 159099,
4086
+ "contentType": "image/jpeg"
4087
+ }
4088
+ }
4089
+ ],
4090
+ "qa": {
4091
+ "status": "source-and-preview-hashes-verified",
4092
+ "previewCount": 3,
4093
+ "artisticApproval": "not-certified",
4094
+ "audioListened": false,
4095
+ "transferCertified": false
4096
+ },
4097
+ "restrictions": [
4098
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
4099
+ "Scene-specific data/count/layout limits are documented in the selected card.",
4100
+ "Opaque output only; no alpha export.",
4101
+ "Voice/music requires timeScale=1 until explicitly retimed.",
4102
+ "Inspect frame boundaries, typography and final audio before delivery."
4103
+ ]
4104
+ },
4105
+ {
4106
+ "id": "waffle-hours",
4107
+ "version": "1.0.0",
4108
+ "title": "40 horas",
4109
+ "mechanism": "waffle-sort-and-flip",
4110
+ "intent": "Tiempo o recursos: unidades que se ordenan por categor\xEDa y se reasignan al automatizar.",
4111
+ "niche": "educaci\xF3n",
4112
+ "style": "p\xF3ster",
4113
+ "duration": 12,
4114
+ "runtime": "canvas",
4115
+ "supportedRatios": [
4116
+ "16:9",
4117
+ "1:1",
4118
+ "9:16"
4119
+ ],
4120
+ "sourceSha256": "8ca044c40b4fbdf590f99d39937996fe04ed1d6f5164d726ed257101cbc06655",
4121
+ "cardSha256": "a1707d3ed1975fb3aa13cd95998d2a3efad7fc444657e2bb46a5e5a0a1477f8c",
4122
+ "card": '# waffle-hours \xB7 Las horas que te devuelve automatizar\n\n**Mecanismo.** 40 cuadros, uno por hora de la semana (`week`: d\xEDas \xD7 horas), se ordenan por categor\xEDa: la categor\xEDa objetivo forma un bloque de `blockCols` columnas y las dem\xE1s, grupos con glifo + "{n} h". El c\xEDrculo gigante "40 horas a la semana" se ti\xF1e por wipe radial del color objetivo y su n\xFAmero rueda a 22. Luego los cuadros de `automate` saltan a una tira bajo su futuro lugar en el bloque (dejan contornos fantasma y sus grupos se tachan); una flecha negra "automatizar" los recorre: cada cuadro gira de canto, cambia de color y sube al bloque, y el c\xEDrculo cuenta 22 \u2192 31. Cada n\xFAmero sale del horario, nunca se escribe dos veces.\n\n**\xDAsala para.** Docentes (ense\xF1ar vs. calificar y papeleo), m\xE9dicos (consulta vs. notas y seguros), despachos (horas facturables vs. administraci\xF3n), agencias (crear vs. juntas y correos), freelancers. **No la uses para.** M\xE1s de ~5 categor\xEDas o semanas de m\xE1s de ~48 h en 16:9 (los grupos no hacen wrap ni avisan), comparar varias semanas, porcentajes.\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| grid | 0.2 | Cuadros entran por fila (0.13 s) y columna (0.035 s) con rebote; d\xEDas a la izquierda; el c\xEDrculo entra 0.05\u20130.75 con el total; leyenda "\u25A0 = 1 hora" (0.5\u20131.0). |\n| sort | 1.7 | Cada categor\xEDa vuela a su grupo (0.16 s entre categor\xEDas, 0.025 s entre cuadros, 0.8 s de vuelo con giro de 90\xB0); los d\xEDas se van; cada grupo muestra glifo, horas y nombre al aterrizar su \xFAltimo cuadro. |\n| wipe | 3.75 | Wipe radial al color objetivo (0.6 s); el total sale y `target.n` entra; el r\xF3tulo cambia a `targetLabel` + `ofTotal`. |\n| detach | 5.5 | Los cuadros automatizables saltan a la tira (0.7 s, 0.04 s entre ellos); contornos fantasma; grupos tachados y en gris; r\xF3tulo de la tira "{gain} h" + lista. |\n| sweep | 6.5 | Flecha: cola fija, la punta llega al primer cuadro en 0.25 s y salta uno cada 0.14 s; cada cuadro gira de canto (0.3 s), cambia de color al quedar de perfil y sube al bloque (0.32 s); c\xEDrculo y r\xF3tulo del bloque cuentan de forma continua. |\n| final | 8.6 | "+{gain} h" y `gainSub` junto a la punta (desde \u22120.3). |\n| end | 12 | No lo usa el c\xF3digo; ~3.4 s de hold del p\xF3ster. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand, duration, fps, timeScale | kit `bauhaus`, 12 s | n\xFAmero del c\xEDrculo en `bg` sobre `ink` y luego sobre el color objetivo (sin `onColor`): el objetivo debe ser oscuro o saturado |\n| categories[] | `{key (1 letra), label, glyph, color}`; glyph \u2208 circle, square, triangle, half, quarter; color = nombre de rol | rol desconocido \u2192 `ink`; glifo desconocido \u2192 no se dibuja |\n| week[] | `{day, hours}`: una letra por hora | letra desconocida \u2192 warning y se omite; dise\xF1ado para 5 \xD7 8 |\n| target | key que crece | \u2014 |\n| automate | keys que se automatizan | sus cuadros deben caer en una sola fila del bloque: si `target.n + gain` cruza de fila, la tira se parte en dos alturas y la flecha (calculada sobre el primero) corre hacia atr\xE1s |\n| blockCols | columnas del bloque | 11 |\n| copy.unit / totalLabel / targetLabel | leyenda / r\xF3tulo del total / r\xF3tulo del objetivo | r\xF3tulos con maxW = 1.6 r (solo avisa) |\n| copy.ofTotal | `{total}` | \u2014 |\n| copy.hours / strip / gain | `{n} h` / `{list}` (labels de `automate` unidos con " + ") / `+{n}` = horas ganadas | \u2014 |\n| copy.arrow / gainSub / note | texto dentro de la flecha (`bg`) / bajo la ganancia / aviso | \u2014 |\n| beats | ver tabla | \u2014 |\n| sound.cues | 19; los 9 `tick` (+0.4\u2026+1.52 cada 0.14 s tras `sweep`) van alineados a 9 cuadros | aj\xFAstalos si cambia `gain` |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- Categor\xEDas, horario, objetivo, qu\xE9 se automatiza, textos; total, horas por grupo, ganancia y conteo final se recalculan.\n- Beats con la voz: `wipe` en "solo 22 ense\xF1ando", `sweep` en "automatiza", `final` en "+9".\n- Roles: el de cada categor\xEDa (`accent`, `accent2`, `pos`, `ink`, `muted` por defecto), `ink` (c\xEDrculo inicial, flecha, leyenda), `bg` (texto del c\xEDrculo y de la flecha), `line` (fantasmas, glifo tachado), `muted`. Fuentes: `display` (c\xEDrculo), `heavy` (horas), `text`.\n\n## Qu\xE9 requiere tocar c\xF3digo\n- Posici\xF3n y radio del c\xEDrculo, columnas del bloque por formato, grupos de m\xE1s de 2 filas o con wrap autom\xE1tico.\n- Unidades distintas de horas por cuadro (el n\xFAmero del c\xEDrculo es el conteo de cuadros).\n- Estilo de la flecha o del giro.\n\n## Formatos\n- 16:9: c\xEDrculo (r 440u) a la izquierda sangrando por arriba e izquierda; cuadr\xEDcula (x 960u) y bloque (x 930u) arriba-derecha; grupos en fila en y 690u.\n- 1:1: c\xEDrculo (r 330u) arriba-izquierda; grupos a la derecha en 2 columnas (filas cada 236u); cuadr\xEDcula y bloque abajo-izquierda (y 668u).\n- 9:16: c\xEDrculo (r 420u) arriba-derecha sangrando; cuadr\xEDcula y bloque en y 1010u; grupos en fila en y 1520u.\n\n## Sonido\npop + 3 ticks en `grid`; whoosh + snap (+1.6) en `sort`; drop en `wipe`; whoosh-short en `detach`; whoosh-short + 9 ticks en `sweep`; chime en `final`. Funciona muda.\n\n## Limitaciones\n- Cifras ilustrativas (semana tipo, `copy.note`).\n- Un grupo usa `ceil(n/2)` columnas: una categor\xEDa con muchas horas se vuelve ancha y empuja a las dem\xE1s fuera del cuadro sin aviso.\n- El n\xFAmero del c\xEDrculo no tiene fit (400 / 250 / 380u seg\xFAn formato): semanas de 100+ cuadros no est\xE1n previstas.\n- El c\xEDrculo sangra del cuadro a prop\xF3sito (p\xF3ster, no tablero); el r\xF3tulo interior s\xED se recorta si es largo.\n\n## Adaptar a otro tema\n- M\xE9dica general (`theme: medical`): `categories [{key:"C",label:"consulta",glyph:"circle",color:"accent"},{key:"N",label:"notas",glyph:"square",color:"accent2"},{key:"S",label:"seguros",glyph:"half",color:"ink"},{key:"J",label:"juntas",glyph:"quarter",color:"muted"}]`, `target "C"`, `automate ["N","S"]`, `targetLabel "horas con pacientes"`, `gainSub "con pacientes"`; `week` con las mismas 5 filas de 8 letras.\n- Agencia (`theme: riso`): keys D dise\xF1o, R reuniones, F facturaci\xF3n, E correo; `target "D"`, `automate ["F","E"]`, `targetLabel "horas creando"`, `arrow "automatizar"`.\n- Despacho legal (`theme: ledger`): `target` = horas facturables, `automate` = captura de tiempo + cobranza, `targetLabel "horas facturables"`, `gainSub "facturables"`.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Sin media (no hay `params.media`).\n',
4123
+ "bundle": {
4124
+ "objectKey": "authored-motion/1.0.0/waffle-hours/e3d0d073b789351fdb7636bee220a3c92437ed57a6b57d13f9e048e516313e88.tar.gz",
4125
+ "sha256": "e3d0d073b789351fdb7636bee220a3c92437ed57a6b57d13f9e048e516313e88",
4126
+ "bytes": 2938059,
4127
+ "fileCount": 58,
4128
+ "contentType": "application/gzip"
4129
+ },
4130
+ "previews": [
4131
+ {
4132
+ "ratio": "wide",
4133
+ "width": 1920,
4134
+ "height": 1080,
4135
+ "duration": 12,
4136
+ "video": {
4137
+ "objectKey": "authored-motion/1.0.0/waffle-hours/previews/f5aa30c5743fd62180a53f3a5bf3fe1706de26ab306bb55a93ccfaee56cdf459.mp4",
4138
+ "sha256": "f5aa30c5743fd62180a53f3a5bf3fe1706de26ab306bb55a93ccfaee56cdf459",
4139
+ "bytes": 1018057,
4140
+ "contentType": "video/mp4"
4141
+ },
4142
+ "poster": {
4143
+ "objectKey": "authored-motion/1.0.0/waffle-hours/previews/0dfe37c939bff26e517a44efee88e875ea3eb8a54fa0d3548fda12bc6c8cd920.jpg",
4144
+ "sha256": "0dfe37c939bff26e517a44efee88e875ea3eb8a54fa0d3548fda12bc6c8cd920",
4145
+ "bytes": 104632,
4146
+ "contentType": "image/jpeg"
4147
+ }
4148
+ },
4149
+ {
4150
+ "ratio": "square",
4151
+ "width": 1080,
4152
+ "height": 1080,
4153
+ "duration": 12,
4154
+ "video": {
4155
+ "objectKey": "authored-motion/1.0.0/waffle-hours/previews/a2372e18fcc1ac490d5008ed2b46a76bbc9c887023047db56b24822b8151654e.mp4",
4156
+ "sha256": "a2372e18fcc1ac490d5008ed2b46a76bbc9c887023047db56b24822b8151654e",
4157
+ "bytes": 770370,
4158
+ "contentType": "video/mp4"
4159
+ },
4160
+ "poster": {
4161
+ "objectKey": "authored-motion/1.0.0/waffle-hours/previews/8dbbba15c055fd5b618e2380b7e0c565d9ee91ccbf0233cda70322d151c63643.jpg",
4162
+ "sha256": "8dbbba15c055fd5b618e2380b7e0c565d9ee91ccbf0233cda70322d151c63643",
4163
+ "bytes": 69633,
4164
+ "contentType": "image/jpeg"
4165
+ }
4166
+ },
4167
+ {
4168
+ "ratio": "tall",
4169
+ "width": 1080,
4170
+ "height": 1920,
4171
+ "duration": 12,
4172
+ "video": {
4173
+ "objectKey": "authored-motion/1.0.0/waffle-hours/previews/3c48d26213441da64d7ef48c05d372c36ef89332a3d057c9aefdc294aae6cdff.mp4",
4174
+ "sha256": "3c48d26213441da64d7ef48c05d372c36ef89332a3d057c9aefdc294aae6cdff",
4175
+ "bytes": 1023649,
4176
+ "contentType": "video/mp4"
4177
+ },
4178
+ "poster": {
4179
+ "objectKey": "authored-motion/1.0.0/waffle-hours/previews/fbf383ccc58abb54521f31c5baae39e2d90bd879e98bcc05bdcc74830a9bfaf1.jpg",
4180
+ "sha256": "fbf383ccc58abb54521f31c5baae39e2d90bd879e98bcc05bdcc74830a9bfaf1",
4181
+ "bytes": 107535,
4182
+ "contentType": "image/jpeg"
4183
+ }
4184
+ }
4185
+ ],
4186
+ "qa": {
4187
+ "status": "source-and-preview-hashes-verified",
4188
+ "previewCount": 3,
4189
+ "artisticApproval": "not-certified",
4190
+ "audioListened": false,
4191
+ "transferCertified": false
4192
+ },
4193
+ "restrictions": [
4194
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
4195
+ "Scene-specific data/count/layout limits are documented in the selected card.",
4196
+ "Opaque output only; no alpha export.",
4197
+ "Voice/music requires timeScale=1 until explicitly retimed.",
4198
+ "Inspect frame boundaries, typography and final audio before delivery."
4199
+ ]
4200
+ },
4201
+ {
4202
+ "id": "clay-store",
4203
+ "version": "1.0.0",
4204
+ "title": "Despu\xE9s de \xABComprar\xBB",
4205
+ "mechanism": "3d-diorama-follow",
4206
+ "intent": "Proceso f\xEDsico de punta a punta: un objeto continuo recorre estaciones en un diorama 3D (Three.js).",
4207
+ "niche": "e-commerce",
4208
+ "style": "3D",
4209
+ "duration": 13,
4210
+ "runtime": "canvas-threejs",
4211
+ "supportedRatios": [
4212
+ "16:9",
4213
+ "1:1",
4214
+ "9:16"
4215
+ ],
4216
+ "sourceSha256": "8f335872ba0430ad838d3d31e2b8ee99b4b642fcb31656d58612070d3314f6ff",
4217
+ "cardSha256": "4f13abe92033b7368fe48b4ca976752d0024dbc341de2bc09b29ea61ded5bee6",
4218
+ "card": '# clay-store \xB7 Despu\xE9s de \xABComprar\xBB\n\n**Mecanismo.** Diorama 3D de plastilina (Three.js en un canvas offscreen, c\xE1mara ortogr\xE1fica isom\xE9trica, compuesto sobre el canvas 2D) donde un solo paquete hace todo el viaje: sale del tel\xE9fono al pulsar \xABComprar\xBB, rueda por una banda, una prensa lo cierra y encinta, salta a la caja abierta de una camioneta, recorre un camino sinuoso y queda junto a la puerta. Cada estaci\xF3n sella su hora en una pastilla colgada de un punto 3D proyectado; el contador suma minutos entre sellos y termina en el total calculado (\xFAltimo \u2212 primero, cruzando medianoche: 23:02 \u2192 14:05 = 15 h 03 min). La c\xE1mara sigue un promedio con retardo de la trayectoria del propio paquete (12 muestras en 0.66 s) y al final se aleja al diorama completo.\n\n**\xDAsala para.** E-commerce: qu\xE9 pasa entre el pago y la puerta. Paqueter\xEDa/\xFAltima milla: tiempo por etapa. Delivery local (panader\xEDa, farmacia): "de tu click a tu casa en X h". **No la uses para.** Procesos con \u2260 4 etapas o sin objeto f\xEDsico (servicios, SaaS); entregas de varios d\xEDas (horas sin fecha, cada tramo < 24 h); mostrar la app real del cliente (pantalla ilustrativa).\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| (inicio) | 0\u20131.1 | Encuadre en el tel\xE9fono; titular 0.15\u20130.7; nota; el cursor entra desde abajo-derecha 0.35\u20130.7. |\n| tap | 1.1 | Click con ripple; el bot\xF3n se hunde y a +0.14 pasa a `pos` con `paid` + check. |\n| pop | 1.3 | El paquete abierto (con el art\xEDculo dentro) sale del tel\xE9fono dando una vuelta; sello 0 a +0.08; entra el contador. |\n| land | 2.0 | Cae en la banda con squash; las franjas de la banda avanzan con \xE9l. C\xE1mara tel\xE9fono \u2192 seguimiento 1.5\u20132.7 (segundos fijos); el titular sale 2.3\u20132.7. |\n| pack | 3.25 | Baja la prensa (0.2 s), cierran las solapas, cinta arriba +0.4 y a los lados +0.52, etiqueta con c\xF3digo de barras y `order` +0.62; sello 1 a +0.32. Luego avanza al final de la banda. |\n| load | 4.95 | Salta a la caja de la camioneta (0.5 s); rebote de suspensi\xF3n. |\n| drive | 5.9 | Recorre la curva Catmull-Rom hasta `arrive` (inOutSine); balanceo \u221D giro \xD7 velocidad, ruedas giran por distancia; sello 2 cuelga bajo el camino a +0.05. |\n| arrive | 9.2 | Frena con cabeceo; la c\xE1mara pasa al encuadre de la casa (arrive\u22120.6 \u2192 hop+0.5). |\n| hop | 9.4 | El paquete salta (0.45 s) al lado de la puerta, nunca delante de ella. |\n| door | 10.0 | La puerta abre (0.45 s, outBack); sello 3 a +0.2; check `pos` con ripple a +0.3. |\n| pull | 10.5 | Vista general (1.15 s); las pastillas se encogen; vuelve el titular (+0.5). |\n| total | 11.35 | La etiqueta pasa a `totalLabel`; borde y valor en `accent` con pulso. Sostiene hasta 13 s (`end` no lo usa el c\xF3digo). |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand, duration, fps, timeScale | kit `clay`, 13 s, 30 fps | \u2014 |\n| copy.headline | titular `display` arriba | wrap 0.36W en 16:9 (~2 l\xEDneas); con velo en 1:1/9:16 |\n| copy.shop / product / price / sizes | pantalla del tel\xE9fono (textura 600 px, sin fit); el reloj de la barra de estado es `stages[0].time` | shop \u2264 ~16 car., price \u2264 ~9; sizes \u2264 4 (la 2.\xAA siempre marcada); el tenis est\xE1 dibujado en c\xF3digo |\n| copy.buy / paid | bot\xF3n antes/despu\xE9s (heavy 64 en textura 512 px; `paid` lleva check) | \u2264 ~11 car., sin fit |\n| copy.order / warehouse | texto de la etiqueta de la caja / letrero del almac\xE9n | \u2264 ~12 / \u2264 ~9 car. |\n| copy.counterLabel / totalLabel | etiqueta mono en MAY\xDASCULAS del contador, antes/despu\xE9s de `total` | 1 l\xEDnea; el panel se ensancha solo |\n| copy.note | aviso de ilustrativo, abajo | 1 l\xEDnea |\n| stages[] | `{label, time "HH:MM"}` sellos; contador y total se calculan (cada tramo mod 24 h) | exactamente 4: anclas, colores y tiempos de sello fijos (con otro n\xFAmero truena o da NaN) |\n| beats.* | ver tabla | mantener el orden; \u2265 ~3 s entre `drive` y `arrive` |\n| sound.cues | 15 cues | ver Sonido |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- Tienda, producto, precio, tallas, bot\xF3n, pedido, letrero, titular, etiquetas del contador.\n- Los 4 sellos (texto y hora): contador y total se recalculan.\n- Beats con la voz: `tap` en "compras", `pack` en "lo empacan", `drive` en "sale", `door` en "llega", `total` en el remate.\n- Roles: `accent` (bot\xF3n, techo, prensa, total), `pos` (pagado, check), `accent2` (cabina, follaje), `series[1\u20135]` (cajas, toldo, cinta, art\xEDculo, casa, vidrios), `surface`/`bg` (fondo y pastillas), `ink`, `muted`, `line`. Fuentes: `display`, `text`, `heavy`, `mono`.\n\n## Qu\xE9 requiere tocar c\xF3digo\n- Otro n\xFAmero de etapas o estaciones (tel\xE9fono, prensa, camino y casa son geometr\xEDa fija).\n- Producto en pantalla y art\xEDculo en la caja (es una esfera gen\xE9rica `series[4]`, no el tenis).\n- Fechas (tramos > 24 h) o formato "H h MM min".\n- Tiempos fijos en segundos: c\xE1mara tel\xE9fono \u2192 seguimiento (1.5\u20132.7), cursor (0.35\u20130.7), salida del titular (2.3\u20132.7). Si mueves `tap`/`pop`/`land` m\xE1s de ~0.4 s, aj\xFAstalos.\n- Trazado del camino, posiciones, \xE1ngulos de c\xE1mara por formato, sombras.\n\n## Formatos\n- 16:9 \u2014 yaw \u221236\xB0, elevaci\xF3n 30\xB0; titular y contador arriba-izquierda sin velo; la vista general ocupa 2\u201398 % \xD7 5\u201396 % del cuadro.\n- 1:1 \u2014 yaw \u221242\xB0, elevaci\xF3n 34\xB0, casa girada \u22120.12 rad; titular con velo; vista general desde 13 % de alto.\n- 9:16 \u2014 yaw \u221263\xB0, elevaci\xF3n 43\xB0, casa girada \u22120.42 rad para que la puerta mire a c\xE1mara; titular y contador centrados; vista general en 25\u201392 % de alto. Zoom ortogr\xE1fico de cada encuadre recalculado por formato; nunca recorta.\n\n## Sonido\nclick en `tap`; pop al salir; thud + whoosh-short al caer; stamp (+0.2, la prensa toca), paper (+0.4, cinta) y pop (+0.62, etiqueta) en `pack`; thud al caer en la caja (`load`+0.5); whoosh al arrancar; thud al llegar; pop en el salto; click + success en la puerta; whoosh en la vista general; chime en el total. Funciona muda: cada sello nombra su etapa.\n\n## Limitaciones\n- Render lento: ~210\u2013305 ms por cuadro 1920\xD71080 (\u2248 80\u2013120 s solo de cuadros por formato, 390 cuadros); requiere WebGL (sombras PCF 2048).\n- La "camioneta" (`van` en c\xF3digo) es una pickup de caja abierta a prop\xF3sito: el paquete tiene que verse todo el trayecto.\n- Horas sin fecha: cada tramo se toma mod 24 h. Tienda, pedido y horarios ilustrativos (`copy.note`).\n- Textos pintados en texturas sin ajuste ni aviso de overflow.\n\n## Adaptar a otro tema\n- Panader\xEDa a domicilio (`theme: cafe`): `shop` "Pan de Barrio", `product` "Caja de conchas", `price` "$180", `warehouse` "HORNO", `stages` Pagado 07:10 / Horneado 07:55 / En ruta 08:20 / Entregado 08:48 \u2192 total 1 h 38 min.\n- Farmacia (`theme: medical`): `headline` "De tu receta a tu puerta.", `buy` "Pedir", `stages` 21:40 / 21:52 / 22:05 / 22:31 \u2192 51 min.\n- Marketplace de muebles (`brand.colors` del cliente): `warehouse` "BODEGA", horas dentro de un mismo d\xEDa o con tramos < 24 h.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas + Three.js r186 vendorizado en `lib/vendor`, licencia MIT, cargado con import map; addons `RoomEnvironment` y `RoundedBoxGeometry`). Sin media externa.\n',
4219
+ "bundle": {
4220
+ "objectKey": "authored-motion/1.0.0/clay-store/a8e8aadaecabb7e1029c0ab4a3a5bac1522847eee10e97929a346b2b76f7efc4.tar.gz",
4221
+ "sha256": "a8e8aadaecabb7e1029c0ab4a3a5bac1522847eee10e97929a346b2b76f7efc4",
4222
+ "bytes": 2945607,
4223
+ "fileCount": 58,
4224
+ "contentType": "application/gzip"
4225
+ },
4226
+ "previews": [
4227
+ {
4228
+ "ratio": "wide",
4229
+ "width": 1920,
4230
+ "height": 1080,
4231
+ "duration": 13,
4232
+ "video": {
4233
+ "objectKey": "authored-motion/1.0.0/clay-store/previews/d6b7c3d6745e749f835ecc832e229c51836f7b8460e150943b205eaa414ac9af.mp4",
4234
+ "sha256": "d6b7c3d6745e749f835ecc832e229c51836f7b8460e150943b205eaa414ac9af",
4235
+ "bytes": 28100532,
4236
+ "contentType": "video/mp4"
4237
+ },
4238
+ "poster": {
4239
+ "objectKey": "authored-motion/1.0.0/clay-store/previews/d31792ffd27e373718034fb45dd0174a24ba26ab3fb19856a8058705cc61008c.jpg",
4240
+ "sha256": "d31792ffd27e373718034fb45dd0174a24ba26ab3fb19856a8058705cc61008c",
4241
+ "bytes": 228529,
4242
+ "contentType": "image/jpeg"
4243
+ }
4244
+ },
4245
+ {
4246
+ "ratio": "square",
4247
+ "width": 1080,
4248
+ "height": 1080,
4249
+ "duration": 13,
4250
+ "video": {
4251
+ "objectKey": "authored-motion/1.0.0/clay-store/previews/b51017dece3ee05159f0dfe90d1aa01f4a76f7a5ba01614b3f700a482d16f9bc.mp4",
4252
+ "sha256": "b51017dece3ee05159f0dfe90d1aa01f4a76f7a5ba01614b3f700a482d16f9bc",
4253
+ "bytes": 15660745,
4254
+ "contentType": "video/mp4"
4255
+ },
4256
+ "poster": {
4257
+ "objectKey": "authored-motion/1.0.0/clay-store/previews/5af265fec3689b33c51018ea9b35a9df15bb7cdfbec8cfe4acaf162b4f0003ba.jpg",
4258
+ "sha256": "5af265fec3689b33c51018ea9b35a9df15bb7cdfbec8cfe4acaf162b4f0003ba",
4259
+ "bytes": 142139,
4260
+ "contentType": "image/jpeg"
4261
+ }
4262
+ },
4263
+ {
4264
+ "ratio": "tall",
4265
+ "width": 1080,
4266
+ "height": 1920,
4267
+ "duration": 13,
4268
+ "video": {
4269
+ "objectKey": "authored-motion/1.0.0/clay-store/previews/eaabeb5dace3b237ad8cacd47be92f4b6027fbe0d466c57f0548fdc87ae776d1.mp4",
4270
+ "sha256": "eaabeb5dace3b237ad8cacd47be92f4b6027fbe0d466c57f0548fdc87ae776d1",
4271
+ "bytes": 26783407,
4272
+ "contentType": "video/mp4"
4273
+ },
4274
+ "poster": {
4275
+ "objectKey": "authored-motion/1.0.0/clay-store/previews/5c932af6683997e1314b39d0236626fd8981d2326b1be4111766523183604ee5.jpg",
4276
+ "sha256": "5c932af6683997e1314b39d0236626fd8981d2326b1be4111766523183604ee5",
4277
+ "bytes": 207639,
4278
+ "contentType": "image/jpeg"
4279
+ }
4280
+ }
4281
+ ],
4282
+ "qa": {
4283
+ "status": "source-and-preview-hashes-verified",
4284
+ "previewCount": 3,
4285
+ "artisticApproval": "not-certified",
4286
+ "audioListened": false,
4287
+ "transferCertified": false
4288
+ },
4289
+ "restrictions": [
4290
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
4291
+ "Scene-specific data/count/layout limits are documented in the selected card.",
4292
+ "Opaque output only; no alpha export.",
4293
+ "Voice/music requires timeScale=1 until explicitly retimed.",
4294
+ "Inspect frame boundaries, typography and final audio before delivery."
4295
+ ]
4296
+ },
4297
+ {
4298
+ "id": "blueprint-build",
4299
+ "version": "1.0.0",
4300
+ "title": "El error cuesta m\xE1s en obra",
4301
+ "mechanism": "plan-to-axonometric",
4302
+ "intent": "Costo de corregir tarde: el mismo error en plano y construido, con el sobrecosto hecho f\xEDsico.",
4303
+ "niche": "construcci\xF3n",
4304
+ "style": "t\xE9cnico",
4305
+ "duration": 13,
4306
+ "runtime": "canvas",
4307
+ "supportedRatios": [
4308
+ "16:9",
4309
+ "1:1",
4310
+ "9:16"
4311
+ ],
4312
+ "sourceSha256": "2aa291304895232c54fe2c41cfacafe17b49a1132a1153b3e1157861d4690ad1",
4313
+ "cardSha256": "299e2cce02c36bc37235738ed61e55f02d4adb8c3194ae169eac12b4bcdf586c",
4314
+ "card": '# blueprint-build \xB7 El error cuesta m\xE1s en obra\n\n**Mecanismo.** Un solo modelo de plano (muros, vanos, columna) se proyecta con un mapa lineal cuya rotaci\xF3n, achatamiento y factor de altura se animan de planta a axonom\xE9trica. El plano se dibuja solo; una nube de revisi\xF3n marca una puerta cuyo abatimiento pega con la columna (el \xE1ngulo de tope se calcula barriendo la hoja contra la columna: hoy 34\xB0). Rama A: la puerta se borra y se redibuja m\xE1s abajo del muro \u2192 1 bloque de costo. Rama B: el mismo dibujo se inclina, los muros suben, la puerta se construye donde estaba dibujada y choca: la columna se agrieta, el muro se marca para demoler y el libro de costos se llena con `round(site/plan)` bloques iguales (76), as\xED el m\xFAltiplo es f\xEDsico. \xC1reas (36.9 / 17.1 / 6.2 m\xB2) y cotas salen de la misma geometr\xEDa.\n\n**\xDAsala para.** Despachos de arquitectura y constructoras: por qu\xE9 pagar la revisi\xF3n de planos. Inmobiliarias en preventa: "cambiar en plano vs en obra". Cursos de supervisi\xF3n de obra/BIM. **No la uses para.** Plantas con otra topolog\xEDa (4 muros + 2 particiones fijos); comparaciones sin un error f\xEDsico concreto; met\xE1foras de software (una casa no es cre\xEDble como "bug").\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| (inicio) | 0\u20131.0 | Rejilla 0\u20130.6; `kicker` se teclea 0.2\u20131.0 con `titleBlock`; nota. |\n| draw | 0.3 | Contorno de muros como una sola l\xEDnea (1.25 s); poch\xE9 +1.0; columnas +1.05; ventanas +1.2; puertas +1.25; ejes +1.3; cotas +1.5 (texto +1.8); cuartos con m\xB2 +1.9. |\n| conflict | 2.8 | El abatimiento pasa a punteado `accent2` desde el \xE1ngulo de tope; nube de revisi\xF3n (0.6 s); tri\xE1ngulo \u03941 +0.45; gu\xEDa a la nota +0.6 (no en 9:16); nota `revision` + `conflict` +0.7. |\n| fixA | 4.1 | La goma baja por el vano (0.5 s); vano viejo rayado +0.6, nuevo corte +0.75, nueva puerta en `accent` +0.85; entra la fila A; la nube sale +1.3. |\n| costA | 5.35 | 1 bloque, "$500" + `planTime`; leyenda `unit` +0.2. |\n| rewind | 6.1 | Se deshace la correcci\xF3n (0.3 s); la puerta en conflicto vuelve en `accent2`; entra la fila B. |\n| tilt | 6.85 | Anotaciones y nota salen antes (\u22120.55 \u2192 \u22120.1); planta \u2192 axonom\xE9trica en 1 s; piso en `surface`; rejilla al 55 %. |\n| rise | 7.75 | Los muros suben (1 s); caras ordenadas por profundidad. |\n| bonk | 8.85 | Acercamiento desde \u22120.6 con marco `detail`; la hoja gira 0.45 s y se detiene en el tope con rebote; destello + grieta en la columna +0.45; tramo a demoler rayado +0.95; `demolish` a mano +1.1. |\n| costB | 9.75 | 76 bloques en 1.2 s; el monto cuenta con los bloques hasta $38,000; `siteTime` +1.1. |\n| ratio | 11.0 | El acercamiento suelta (\u22120.75 \u2192 \u22120.05); sello `times` "76\xD7 m\xE1s caro"; sale la nota de B. |\n| message | 11.35 | Sale el kicker; entra la frase con ambos montos. Sostiene 1.65 s. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand, duration, fps, timeScale | kit `blueprint` (oscuro), 13 s, 30 fps | \u2014 |\n| copy.kicker / titleBlock | r\xF3tulos mono arriba-izquierda | 1 l\xEDnea; "ESC 1:50" es decorativo (el plano se ajusta al escenario) |\n| copy.rooms[] | `{name, at:[x,y] en m}` etiqueta + m\xB2 calculado | exactamente 3 y en orden fijo: sala (izq.), rec\xE1mara (der.-arriba), ba\xF1o (der.-abajo); `at` no se recalcula |\n| copy.axisX / axisY | burbujas de los ejes que pasan por la columna | 1\u20132 car. |\n| copy.revision / conflict | nota \u03941; `{deg}` = \xE1ngulo de tope calculado | wrap balanceado al ancho de la nota (214u en 1:1 \u2192 ~4 l\xEDneas) |\n| copy.branchA/B, branchANote/BNote | t\xEDtulos de fila y notas a mano | notas ocultas en 1:1 |\n| copy.demolish / detail / unit / times / message / note | `{a}` `{b}` montos, `{n}` m\xFAltiplo | message wrap a W \u2212 2x |\n| plan.width/depth/ext/int/height | m; muros N S W E + P1 (vertical en `partitionX`) + P2 (horizontal en `partitionY`, de P1 a E) | dintel 2.1 m y alf\xE9izar 0.9 m fijos en c\xF3digo |\n| plan.column | `{x, y, size}` | debe caer en el barrido de la puerta; si no, tope = 90\xB0 y la historia se cae |\n| plan.door / fix | puerta en conflicto y correcci\xF3n, ambas en P1 (`from`, `width`, `hinge`); abren hacia +x | `fix` sobre muro macizo de P1 (si no, no hay rayado de demolici\xF3n) |\n| plan.openings[] | `{wall N|S|W|E|P1|P2, from, to, type window|door, hinge, swing}` | sin traslapes |\n| cost.plan / site / planTime / siteTime | bloques = round(site/plan), filas de 19; la fila B muestra bloques \xD7 plan | `site` m\xFAltiplo de `plan` (si no, la fila B y la frase difieren); 76 (4 filas) es el m\xE1ximo en 1:1, ~95 en 9:16, ~150 en 16:9 |\n| beats / sound.cues | ver tabla / 21 cues | 5 `tick` fijos en `costB` |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- Montos y tiempos: bloques, contador, m\xFAltiplo y frase se recalculan.\n- Dimensiones, columna, puertas y vanos: cotas, \xE1reas y \xE1ngulo de tope se recalculan (revisa el still: el tope debe ser < 90\xB0).\n- Textos de r\xF3tulos, cuartos, notas y frase.\n- Beats con la voz: `conflict` en "un error", `costA` en "en el plano", `bonk` en "en obra", `ratio`/`message` en el remate.\n- Roles: `accent` (revisi\xF3n, rama A, sello), `accent2` (conflicto, rama B, demolici\xF3n, grieta), `ink` (l\xEDneas), `surface` (piso), `line` (rejilla), `muted`, `bg`. Fuentes: `mono`, `hand`, `display`, `heavy`, `text`.\n\n## Qu\xE9 requiere tocar c\xF3digo\n- Otra topolog\xEDa (m\xE1s particiones, \u2260 3 cuartos, muros no ortogonales, segundo nivel).\n- Puerta en conflicto fuera de P1 o abriendo hacia \u2212x; m\xE1s de una columna.\n- \xC1ngulos de la axonom\xE9trica (\u03C1 0.87 rad, q 0.76, zk 0.62), zoom del acercamiento (1.75/1.65/1.7), escenario por formato.\n- Moneda ("$" + en-US), m\xB2, cotas con 2 decimales, 19 bloques por fila.\n\n## Formatos\n- 16:9 \u2014 escenario a la izquierda (1080\xD7870u); nota \u03941 y filas A/B apiladas en la columna derecha (x 1240u, 600u de ancho).\n- 1:1 \u2014 escenario 800\xD7610u; nota en columna estrecha a la derecha (214u); filas A y B lado a lado abajo (y 790u); sin notas de rama.\n- 9:16 \u2014 escenario arriba (1000\xD7900u desde y 230u); nota debajo (y 1190u); filas a todo el ancho (y 1330u y 1530u, bloques al 84 %); sin gu\xEDa \u03941 \u2192 nota.\n\n## Sonido\npaper + scribble al dibujar; tick con las cotas; 2 scribble en la nube; scribble con la goma y snap al quedar la nueva puerta; coin en `costA`; whoosh-short al rebobinar; whoosh al inclinar; riser al subir; thud + crack en el golpe (+0.4/+0.45, el impacto es a +0.45); 5 tick + coin al llenar B; stamp en el m\xFAltiplo; swell en la frase. Funciona muda.\n\n## Limitaciones\n- Proyecto, plano y costos ilustrativos (`copy.note`); el m\xFAltiplo es la divisi\xF3n de los dos montos, no un estudio.\n- Topolog\xEDa y orden de cuartos fijos; el tope solo se calcula para la puerta de P1 contra la columna.\n- La grieta se dibuja en la cara +y de la columna (visible con la rotaci\xF3n actual).\n\n## Adaptar a otro tema\n- Remodelaci\xF3n de oficina: `rooms` "OFICINA" / "SALA DE JUNTAS" / "BA\xD1O", `cost` 800 / 44000 (55 bloques, "55\xD7 m\xE1s caro"), `planTime` "2 h", `siteTime` "10 d\xEDas", `kicker` "PLANO A-201 \xB7 OFICINA CENTRO".\n- Preventa de departamentos: `branchA` "En preventa", `branchB` "Ya entregado", `branchBNote` "se rompe y se rehace", `cost` 1200 / 60000 (50\xD7).\n- Despacho con su marca (`brand.colors`): mover `plan.column` o `plan.door.from`, ajustar `rooms[].at` a mano y verificar en stills que `{deg}` < 90.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas 2D; proyecci\xF3n propia, sin Three.js). Sin media externa.\n',
4315
+ "bundle": {
4316
+ "objectKey": "authored-motion/1.0.0/blueprint-build/45043399b99838219c875078ded430b048c34ad65450f7422fc687da46e0107c.tar.gz",
4317
+ "sha256": "45043399b99838219c875078ded430b048c34ad65450f7422fc687da46e0107c",
4318
+ "bytes": 2945379,
4319
+ "fileCount": 58,
4320
+ "contentType": "application/gzip"
4321
+ },
4322
+ "previews": [
4323
+ {
4324
+ "ratio": "wide",
4325
+ "width": 1920,
4326
+ "height": 1080,
4327
+ "duration": 13,
4328
+ "video": {
4329
+ "objectKey": "authored-motion/1.0.0/blueprint-build/previews/1f460ec80f618cfd2f83ea3de431137705c6c36f172807e0848398f68bbce644.mp4",
4330
+ "sha256": "1f460ec80f618cfd2f83ea3de431137705c6c36f172807e0848398f68bbce644",
4331
+ "bytes": 2970249,
4332
+ "contentType": "video/mp4"
4333
+ },
4334
+ "poster": {
4335
+ "objectKey": "authored-motion/1.0.0/blueprint-build/previews/8a501bb0929a90b90e2aa74651843d430436b5ac5962078098b4629c03a6e727.jpg",
4336
+ "sha256": "8a501bb0929a90b90e2aa74651843d430436b5ac5962078098b4629c03a6e727",
4337
+ "bytes": 227890,
4338
+ "contentType": "image/jpeg"
4339
+ }
4340
+ },
4341
+ {
4342
+ "ratio": "square",
4343
+ "width": 1080,
4344
+ "height": 1080,
4345
+ "duration": 13,
4346
+ "video": {
4347
+ "objectKey": "authored-motion/1.0.0/blueprint-build/previews/d9db204e5818a6012c1715f1a2fe6ef53bfa718579fd77f498c22cbbb4811b96.mp4",
4348
+ "sha256": "d9db204e5818a6012c1715f1a2fe6ef53bfa718579fd77f498c22cbbb4811b96",
4349
+ "bytes": 2240908,
4350
+ "contentType": "video/mp4"
4351
+ },
4352
+ "poster": {
4353
+ "objectKey": "authored-motion/1.0.0/blueprint-build/previews/070333fe3ca99936ba422951f04cf052498d63178db7853720f06225237eed62.jpg",
4354
+ "sha256": "070333fe3ca99936ba422951f04cf052498d63178db7853720f06225237eed62",
4355
+ "bytes": 162344,
4356
+ "contentType": "image/jpeg"
4357
+ }
4358
+ },
4359
+ {
4360
+ "ratio": "tall",
4361
+ "width": 1080,
4362
+ "height": 1920,
4363
+ "duration": 13,
4364
+ "video": {
4365
+ "objectKey": "authored-motion/1.0.0/blueprint-build/previews/f6633b4a1bce4f51c3b3ddd9a4b83eeae8a1f22272474b36afee4a880387016d.mp4",
4366
+ "sha256": "f6633b4a1bce4f51c3b3ddd9a4b83eeae8a1f22272474b36afee4a880387016d",
4367
+ "bytes": 2898496,
4368
+ "contentType": "video/mp4"
4369
+ },
4370
+ "poster": {
4371
+ "objectKey": "authored-motion/1.0.0/blueprint-build/previews/d5e77a6b01ec9ea94dc399e30ba81ed7f2f5296c9555f1ac522ac12bc27177f8.jpg",
4372
+ "sha256": "d5e77a6b01ec9ea94dc399e30ba81ed7f2f5296c9555f1ac522ac12bc27177f8",
4373
+ "bytes": 238997,
4374
+ "contentType": "image/jpeg"
4375
+ }
4376
+ }
4377
+ ],
4378
+ "qa": {
4379
+ "status": "source-and-preview-hashes-verified",
4380
+ "previewCount": 3,
4381
+ "artisticApproval": "not-certified",
4382
+ "audioListened": false,
4383
+ "transferCertified": false
4384
+ },
4385
+ "restrictions": [
4386
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
4387
+ "Scene-specific data/count/layout limits are documented in the selected card.",
4388
+ "Opaque output only; no alpha export.",
4389
+ "Voice/music requires timeScale=1 until explicitly retimed.",
4390
+ "Inspect frame boundaries, typography and final audio before delivery."
4391
+ ]
4392
+ },
4393
+ {
4394
+ "id": "heartbeat",
4395
+ "version": "1.0.0",
4396
+ "title": "Ritmo en reposo",
4397
+ "mechanism": "trace-compresses-to-chart",
4398
+ "intent": "Salud y progreso: una se\xF1al viva que se comprime en tendencia semanal y cambia el pulso.",
4399
+ "niche": "salud",
4400
+ "style": "datos",
4401
+ "duration": 11,
4402
+ "runtime": "canvas",
4403
+ "supportedRatios": [
4404
+ "16:9",
4405
+ "1:1",
4406
+ "9:16"
4407
+ ],
4408
+ "sourceSha256": "8cd996582ce9450f2746d580972898bbd532c049b4dd4c9908cd103565ec7a9c",
4409
+ "cardSha256": "5ace46dc87a06e16280b556e1b01e60e1d36466ffeaeaa03024672535ad72ff3",
4410
+ "card": '# heartbeat \xB7 Ritmo en reposo\n\n**Mecanismo.** Una sola se\xF1al continua `ecg(beatsAt(\u03C4))` cubre 8 semanas de tiempo de datos \u03C4; cada semana late a su propio lpm y su l\xEDnea base ya est\xE1 a la altura de ese lpm en la gr\xE1fica. La pantalla mapea \u03C4 con un factor px/s que cae de forma logar\xEDtmica (~10\u2076\xD7) de velocidad de papel a una semana por banda; cada columna de p\xEDxeles dibuja el m\xEDn/m\xE1x de la se\xF1al que cubre, as\xED la onda se vuelve su propia envolvente y despu\xE9s cada semana colapsa en su punto. Consecuencia: los puntos forman la tendencia; el coraz\xF3n baja al \xFAltimo ritmo en 3 latidos, el lector rueda 72 \u2192 63 y la ca\xEDda (\u22129) se mide sobre la gr\xE1fica. Todo n\xFAmero sale de `weeks`.\n\n**\xDAsala para.** Wearables y apps de fitness: progreso del pulso en reposo. Rehabilitaci\xF3n cardiaca o programas de nutrici\xF3n (con la nota de no diagn\xF3stico). Coaches de running: "lo que cambi\xF3 en 8 semanas". **No la uses para.** M\xE9tricas que suben (flecha y signo asumen bajada); valores de 3 d\xEDgitos (lector de 2); otras se\xF1ales (respiraci\xF3n, glucosa: la onda es PQRST cardiaca); mostrar un ECG real o un diagn\xF3stico.\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| (inicio) | 0\u20130.75 | Panel 0\u20130.5; kicker, coraz\xF3n y lector 0.1\u20130.6; el trazo arranca en 0.5 s (`live` \u2212 0.3\xB760/weeks[0]) con punto gu\xEDa brillante. |\n| live | 0.75 | Primer pico R; el coraz\xF3n late en cada R a `weeks[0]` lpm sobre papel de ECG. |\n| zoom | 3.9 | 2 s de compresi\xF3n logar\xEDtmica; el papel se desvanece; etiqueta `week` sigue al punto (+0.35); rejilla lpm +0.6\u20131.4; separadores y `weekShort` entran al aparecer cada semana. |\n| condense | 6.2 | En 0.9 s las columnas de cada semana colapsan en su punto (pop escalonado); el t\xEDtulo del panel corta a `panelChart`; sale el punto gu\xEDa. |\n| trend | 7.05 | L\xEDnea de tendencia (0.8 s); valor inicial +0.2, final +0.7. |\n| slow | 8.0 | Los 3 latidos siguientes bajan al \xFAltimo ritmo; el lector rueda como od\xF3metro (1 s); el \xFAltimo punto late con el coraz\xF3n; referencia punteada + flecha `pos` con "\u22129" +0.6\u20131.1; insignia `delta` +0.95. |\n| message | 9.05 | Frase calculada; sale el kicker (en 9:16 se queda y la frase va abajo). Sostiene ~2 s; `end` no lo usa el c\xF3digo. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand, duration, fps, timeScale | kit `medical`, 11 s, 30 fps | \u2014 |\n| copy.kicker / panelLive / panelChart | r\xF3tulos mono (paneles en MAY\xDASCULAS) | 1 l\xEDnea |\n| copy.week / weekShort / readoutWeek | `{n}` = semana | cortos |\n| copy.unit / delta / message | `{d}` = weeks[0] \u2212 \xFAltimo; `{a}` `{b}` `{n}` salen de `weeks` | message wrap a W \u2212 2x |\n| copy.note | aviso "no es diagn\xF3stico" | mantenerlo |\n| weeks[] | lpm promedio por semana; tendencia, ca\xEDda, frase, separadores | \u2265 2; enteros 10\u201399; a la baja; dentro de `scale` |\n| scale.min / max / ticks | eje lpm de la gr\xE1fica | puntos fuera del rango salen del panel |\n| ecg.speed | velocidad de papel (px/s a 1080) | tope: el trazo llena ~82 % del plot antes de `zoom` \u2192 hoy 287/137/203 px/s (16:9/1:1/9:16); 300 no manda |\n| ecg.mv | alto del pico R en px a 1080 (140) | \u2014 |\n| beats.* | ver tabla | mover `live` desplaza los blips juntos; los dem\xE1s no |\n| sound.cues | 21 cues; los 14 `blip` est\xE1n puestos a mano en los R calculados | ver Limitaciones |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- `weeks` y `scale`: tendencia, valores, ca\xEDda, insignia, frase y separadores se recalculan (luego recalcula blips).\n- Textos del panel, unidad, nota, kicker.\n- Beats con la voz: `live` en "tu coraz\xF3n", `zoom` en "en 8 semanas", `slow` en "baj\xF3", `message` en el remate.\n- Roles: `accent` (trazo, coraz\xF3n, puntos, tendencia), `pos` (flecha, insignia), `surface` (panel), `line` (rejilla), `ink`, `muted`, `bg`. Fuentes: `display` (n\xFAmero, frase), `heavy`, `mono`, `text`.\n\n## Qu\xE9 requiere tocar c\xF3digo\n- M\xE9tricas que suben (flecha fija hacia abajo, "\u2212" fijo en la gr\xE1fica) o de 3 d\xEDgitos (od\xF3metro de 2).\n- Otra forma de onda, otra unidad que no sea semana (`WEEK` = 604 800 s), otro n\xFAmero de latidos de transici\xF3n (3).\n- Posiciones de coraz\xF3n/lector/insignia (`S.ui`) y del plot (`S.plot`) por formato.\n- Generar los blips desde `S.rTimes` en vez de params (hoy no hay `cues(S)`).\n\n## Formatos\n- 16:9 \u2014 plot a la izquierda (190\u20131380u \xD7 250\u2013880u); columna derecha (x 1650u) con coraz\xF3n, n\xFAmero 170u e insignia; la frase reemplaza al kicker arriba.\n- 1:1 \u2014 plot 120\u2013690u \xD7 300\u2013880u; columna derecha estrecha (x 935u, coraz\xF3n 62u, n\xFAmero 118u).\n- 9:16 \u2014 coraz\xF3n y n\xFAmero arriba (y 330u / 610u), plot en la mitad inferior (910\u20131550u); frase abajo (y 1700u) y el kicker se queda.\n\n## Sonido\n14 `blip` en los picos R (gain 0.5; 0.25 durante el zoom; 0.4\u20130.45 al final, siguiendo la desaceleraci\xF3n); riser en `zoom`+0.3; whoosh-short + 3 tick al condensar; pop con el valor final; success con la insignia; swell en la frase. Funciona muda.\n\n## Limitaciones\n- **Blips fijos en params**: los offsets desde `live` (0, 0.8333 \u2026 7.5, 8.3696, 9.2787, 10.2311) son los R calculados para 72 \u2192 63 lpm con `slow` = 8.0. Si cambias `weeks[0]`, el \xFAltimo valor o `slow`, el coraz\xF3n sigue bien pero los blips se desfasan. Recalcula: R\u2080 = `live`; R\u1D62\u208A\u2081 = R\u1D62 + 60/lpm, con lpm = weeks[0] hasta el primer R \u2265 `slow`, luego lerp(weeks[0], \xFAltimo, n/3) para n = 1, 2, 3 (y \xFAltimo despu\xE9s).\n- Datos ilustrativos; la onda es sint\xE9tica (gaussianas PQRST), no un ECG real; la rejilla no est\xE1 calibrada en mV.\n- Solo tendencias a la baja; lector de 2 d\xEDgitos; la curva interpola linealmente entre semanas, no hay ruido de medici\xF3n.\n\n## Adaptar a otro tema\n- App de running: `weeks` [68, 67, 66, 64, 63, 62, 60, 59], `scale` {min 55, max 75, ticks [55, 60, 65, 70, 75]}, `kicker` "PULSO EN REPOSO \xB7 PLAN 10K"; recalcula los blips (intervalo inicial 60/68 = 0.8824 s).\n- Rehabilitaci\xF3n cardiaca (12 semanas): `weeks` de 78 a 66, `scale` {min 60, max 85, ticks [60, 65, 70, 75, 80, 85]}, `message` "Tu ritmo en reposo baj\xF3 de {a} a {b} en {n} semanas de rehabilitaci\xF3n.", `note` con el aviso de la cl\xEDnica.\n- Programa de mindfulness: `weeks` [74, 73, 71, 70, 68, 67], `kicker` "6 SEMANAS DE RESPIRACI\xD3N", `theme: sage`.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Sin media externa.\n',
4411
+ "bundle": {
4412
+ "objectKey": "authored-motion/1.0.0/heartbeat/53b2424c992e747ef9ebd58027f9045679255ef8ab19b63ba51f4077c185f669.tar.gz",
4413
+ "sha256": "53b2424c992e747ef9ebd58027f9045679255ef8ab19b63ba51f4077c185f669",
4414
+ "bytes": 2939399,
4415
+ "fileCount": 58,
4416
+ "contentType": "application/gzip"
4417
+ },
4418
+ "previews": [
4419
+ {
4420
+ "ratio": "wide",
4421
+ "width": 1920,
4422
+ "height": 1080,
4423
+ "duration": 11,
4424
+ "video": {
4425
+ "objectKey": "authored-motion/1.0.0/heartbeat/previews/70200e79a56c20a5fe0a056ca8329ce4958702ee2fbdb7aba2dea1c71c8ae561.mp4",
4426
+ "sha256": "70200e79a56c20a5fe0a056ca8329ce4958702ee2fbdb7aba2dea1c71c8ae561",
4427
+ "bytes": 1645646,
4428
+ "contentType": "video/mp4"
4429
+ },
4430
+ "poster": {
4431
+ "objectKey": "authored-motion/1.0.0/heartbeat/previews/ffa93e5b4fc8f93edff8407292009186d8402bf16a97e00adb3d6d79b11aab87.jpg",
4432
+ "sha256": "ffa93e5b4fc8f93edff8407292009186d8402bf16a97e00adb3d6d79b11aab87",
4433
+ "bytes": 102565,
4434
+ "contentType": "image/jpeg"
4435
+ }
4436
+ },
4437
+ {
4438
+ "ratio": "square",
4439
+ "width": 1080,
4440
+ "height": 1080,
4441
+ "duration": 11,
4442
+ "video": {
4443
+ "objectKey": "authored-motion/1.0.0/heartbeat/previews/ff5e4834b43e4032b724c13c75d8ac79dc13556483cfcceef5f5cd73ce098a5b.mp4",
4444
+ "sha256": "ff5e4834b43e4032b724c13c75d8ac79dc13556483cfcceef5f5cd73ce098a5b",
4445
+ "bytes": 1060154,
4446
+ "contentType": "video/mp4"
4447
+ },
4448
+ "poster": {
4449
+ "objectKey": "authored-motion/1.0.0/heartbeat/previews/33993a97316e61767d46629b1e6f0095de1d3a125b42376d2dbe51d22870b2bd.jpg",
4450
+ "sha256": "33993a97316e61767d46629b1e6f0095de1d3a125b42376d2dbe51d22870b2bd",
4451
+ "bytes": 72975,
4452
+ "contentType": "image/jpeg"
4453
+ }
4454
+ },
4455
+ {
4456
+ "ratio": "tall",
4457
+ "width": 1080,
4458
+ "height": 1920,
4459
+ "duration": 11,
4460
+ "video": {
4461
+ "objectKey": "authored-motion/1.0.0/heartbeat/previews/d916d9c3543e9592d29f86155a18b86036796456674cab2cc54a5d5312762169.mp4",
4462
+ "sha256": "d916d9c3543e9592d29f86155a18b86036796456674cab2cc54a5d5312762169",
4463
+ "bytes": 1512066,
4464
+ "contentType": "video/mp4"
4465
+ },
4466
+ "poster": {
4467
+ "objectKey": "authored-motion/1.0.0/heartbeat/previews/f06f51abe9ac272c8db0d68670ae8492d7b03d24c69a1740912592df5f61dad5.jpg",
4468
+ "sha256": "f06f51abe9ac272c8db0d68670ae8492d7b03d24c69a1740912592df5f61dad5",
4469
+ "bytes": 105651,
4470
+ "contentType": "image/jpeg"
4471
+ }
4472
+ }
4473
+ ],
4474
+ "qa": {
4475
+ "status": "source-and-preview-hashes-verified",
4476
+ "previewCount": 3,
4477
+ "artisticApproval": "not-certified",
4478
+ "audioListened": false,
4479
+ "transferCertified": false
4480
+ },
4481
+ "restrictions": [
4482
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
4483
+ "Scene-specific data/count/layout limits are documented in the selected card.",
4484
+ "Opaque output only; no alpha export.",
4485
+ "Voice/music requires timeScale=1 until explicitly retimed.",
4486
+ "Inspect frame boundaries, typography and final audio before delivery."
4487
+ ]
4488
+ },
4489
+ {
4490
+ "id": "travel-journal",
4491
+ "version": "1.0.0",
4492
+ "title": "7 d\xEDas en Oaxaca",
4493
+ "mechanism": "journal-route-stamps",
4494
+ "intent": "Itinerarios: ruta a mano sobre papel kraft, tarjetas con washi, sellos por d\xEDa y ticket final.",
4495
+ "niche": "turismo",
4496
+ "style": "ilustraci\xF3n",
4497
+ "duration": 14,
4498
+ "runtime": "canvas",
4499
+ "supportedRatios": [
4500
+ "16:9",
4501
+ "1:1",
4502
+ "9:16"
4503
+ ],
4504
+ "sourceSha256": "d4a3eda42af98049f0d6ade2c8094c7643a5babe465766a4e7e8e1e7026ca7c0",
4505
+ "cardSha256": "b705ac2e6b39cf5e08336df4ee00b3518be66c51365df9ca6eb52c7aa2e9a27f",
4506
+ "card": '# travel-journal \xB7 Itinerario en un diario de viaje\n\n**Mecanismo.** Una p\xE1gina de diario kraft con mapa a mano (costa doble, olas, sierras, rosa de los vientos) generado con semilla y que esquiva los elementos de la historia. Un pin cae en la primera parada; la ruta punteada se dibuja tramo a tramo y la c\xE1mara sigue la punta (un encuadre por parada, con un leve zoom-out en tramos largos). En cada parada cae una tarjeta-foto ilustrada (vi\xF1eta procedural) que se fija con cinta washi y un sello de goma "D\xCDA n" golpea su borde (la c\xE1mara se sacude). Cada tramo suma sus km al contador y cada sello avanza el d\xEDa. La \xFAltima parada no lleva foto: el pin se abre en un boleto con d\xEDas, CTA, km totales y c\xF3digo de barras. Cierra con un pull-back a la p\xE1gina completa sobre el escritorio.\n\n**\xDAsala para.** Agencias y tour operadores (itinerario de 7 d\xEDas), hoteles con experiencias, rutas gastron\xF3micas o de vino, road trips, creadores de viaje que venden una gu\xEDa. **No la uses para.** Itinerarios de otro n\xFAmero de paradas (los layouts est\xE1n hechos a mano para 7), mapas o distancias reales (la geograf\xEDa es suelta a prop\xF3sito: sierra al norte, costa al sur), kits oscuros.\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| (mapa) | 0\u20131.4 | Costa, olas, monta\xF1as, rosa y r\xF3tulos se dibujan en orden sembrado; aros de l\xE1piz punteados en las paradas planeadas. |\n| title | 0.15 | T\xEDtulo letra por letra (0.035 s c/u); subrayado a mano bajo `underline` de +0.55 a +1.05. |\n| d1 | 1.0 | Parada 1: el pin cae (0.25 s) con ripple y la parada se entinta; tarjeta +0.05 (cae 0.34 s), cinta +0.34, sello +0.58, nota a mano +0.2 (se escribe en 0.7 s); HUD "D\xCDA n de 7 \xB7 KM" entra a +0.2. |\n| step | 1.5 | Separaci\xF3n entre llegadas: parada i en `d1 + i\xB7step`; el tramo sale a +`timing.leave` (0.72) y llega a la siguiente; los km cuentan durante el tramo. |\n| (parada 7) | 10.0 | El pin se abre en el boleto (0.55 s, con rebote); cinta; sello a +0.73. |\n| pull | 10.95 | Pull-back de 1.45 s a la p\xE1gina entera; el HUD se va. |\n| end | 14 | No lo usa el c\xF3digo; ~1.6 s de hold del itinerario completo. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand, duration, fps, timeScale | kit `journal`, 14 s | cintas y vi\xF1etas usan `series[2]` y `series[4]` (`series` \u2265 5); sellos en `multiply`: kit claro |\n| thousands | separador de miles de los km | \u2014 |\n| stops[] | `{name, km, scene, note?}`; `km` = largo del tramo que llega (la 1.\xAA = 0); total y d\xEDas se calculan | exactamente 7: con menos la escena falla (la 7.\xAA posici\xF3n es el boleto); con m\xE1s, las extra reutilizan la posici\xF3n del boleto y se enciman. `scene` \u2208 market, ruins, mountains, cliff, clouds, sea (otra \u2192 sea; la \xFAltima no usa vi\xF1eta). `note` solo se dibuja en las paradas 1, 4 y 6 (\xFAnicas con lugar en el layout); la 6 lleva flecha |\n| copy.title / underline | t\xEDtulo (fit al ancho de la p\xE1gina, m\xEDn. 60) / subcadena subrayada (\xFAltima aparici\xF3n; el punto final no se subraya) | 1 l\xEDnea |\n| copy.day / of / km | HUD y sello (`day` va en cada sello) / `{n}` = paradas / r\xF3tulo de km | \u2014 |\n| copy.sea / disclaimer | r\xF3tulo del mar / aviso en la esquina de la p\xE1gina | mantener el aviso |\n| copy.stubKicker | `{from}` = 1.\xAA palabra de la 1.\xAA parada, `{to}` = nombre de la \xFAltima; en MAY\xDAS | maxW solo avisa |\n| copy.stubDays / cta / stubTotals | r\xF3tulo bajo el n\xFAmero de d\xEDas / CTA (fit, m\xEDn. 30) / `{km}` `{days}` | \u2014 |\n| beats | `title`, `d1`, `step`, `pull`, `end` | la \xFAltima llegada (`d1 + 6\xB7step`) debe quedar \u2265 0.9 s antes de `pull` |\n| timing | `card`, `tape`, `stamp`, `leave` relativos a cada llegada | `leave` < `step` |\n| sound.cues | 4 manuales + los de `cues(S)` | \u2014 |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- Nombres, km, vi\xF1eta por parada, notas (paradas 1/4/6), t\xEDtulo, CTA, textos; km, d\xEDas y boleto se recalculan.\n- Ritmo: `step` y `timing` con la voz (un d\xEDa por frase).\n- Roles: `accent` (ruta, pin, subrayado, flecha del norte, lado izquierdo del boleto), `accent2` (sellos, mar, l\xEDnea interior de costa), `pos`/`series[2]`/`series[4]` (cintas), `bg`+`line` (kraft), `surface` (tarjetas, boleto, HUD), `ink`, `muted`. Fuentes: `display` (t\xEDtulo, d\xEDas, CTA), `hand` (notas, pies de foto, mar, "N"), `heavy`, `mono` (km, aviso), `text`.\n\n## Qu\xE9 requiere tocar c\xF3digo\n- Otro n\xFAmero de paradas (`LAYOUTS`: posici\xF3n, tarjeta, sello y nota por parada y por formato).\n- Fotos reales en las tarjetas (hoy vi\xF1etas: mercado con papel picado, pir\xE1mide escalonada, sierra con pinos, cascada petrificada, mar de nubes con caba\xF1a, playa con palma).\n- Geograf\xEDa del mapa (costa al sur, sierras al norte), la "N" fija, el boleto como bot\xF3n real.\n\n## Formatos\n- 16:9: p\xE1gina 3400\xD72100 (pliego con canal central); HUD en una banda de 108u arriba.\n- 1:1: p\xE1gina 2600\xD72600, tarjetas al 90 %; banda HUD 104u.\n- 9:16: p\xE1gina 2100\xD73400, banda HUD 236u; HUD m\xE1s grande.\n- Paradas, tarjetas, sellos y notas colocados a mano por formato; `layoutCheck` avisa en `getDiagnostics()` solapamientos, elementos fuera de la p\xE1gina y tramos que cruzan tarjetas.\n\n## Sonido\npaper + scribble (+0.55) en `title`; generados: pop antes de la 1.\xAA llegada, paper por tarjeta, stamp por sello, scribble al salir cada tramo, chime 0.9 s tras el boleto; whoosh + swell en `pull`. Funciona muda.\n\n## Limitaciones\n- Ruta, mapa y distancias ilustrativos (`copy.disclaimer`); vi\xF1etas dibujadas, no fotos.\n- Coste: prerender de la p\xE1gina (hasta 3400 px), grano por cuadro y `blur` en las sombras de los sellos.\n- La c\xE1mara depende del layout: textos de nota muy largos cambian el encuadre y pueden chocar con el mapa (revisa los warnings).\n\n## Adaptar a otro tema\n- Cusco (`theme: journal`): `title "7 d\xEDas en Cusco."`, `underline "Cusco."`, `stops` Cusco centro (0, market) \xB7 Sacsayhuam\xE1n (3, ruins) \xB7 Valle Sagrado (60, mountains) \xB7 Moray (45, cliff) \xB7 Ollantaytambo (40, clouds) \xB7 Machu Picchu (45, ruins) \xB7 Aguas Calientes (8, ticket); el mapa siempre dibuja costa: `copy.sea ""` o un r\xF3tulo neutro.\n- Ruta del vino (`theme: cafe`): `title "7 d\xEDas en el Valle."`, `underline "Valle."`, `stops` Ensenada (0, market) \xB7 La Bufadora (35, cliff) \xB7 Valle de Guadalupe (40, mountains) \xB7 San Antonio de las Minas (12, clouds) \xB7 El Porvenir (10, mountains) \xB7 Puerto Nuevo (60, sea) \xB7 Rosarito (20, ticket), `cta "Reserva tu cata"`, `stubTotals "{km} km \xB7 {days} d\xEDas de vino"`.\n- Retiro de bienestar (`theme: sage`): `title "7 d\xEDas para bajar el ritmo."`, `underline "ritmo."`, notas "yoga al amanecer" / "temazcal" / "sin celular \u2605", `cta "Aparta tu lugar"`.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Sin media (no hay `params.media`): p\xE1gina, mapa, vi\xF1etas, sellos y boleto se generan con c\xF3digo y semillas.\n',
4507
+ "bundle": {
4508
+ "objectKey": "authored-motion/1.0.0/travel-journal/c21540091c905df61b235042e8fc2678da8105846196ebdc4c5ef44d6206db8c.tar.gz",
4509
+ "sha256": "c21540091c905df61b235042e8fc2678da8105846196ebdc4c5ef44d6206db8c",
4510
+ "bytes": 2948210,
4511
+ "fileCount": 58,
4512
+ "contentType": "application/gzip"
4513
+ },
4514
+ "previews": [
4515
+ {
4516
+ "ratio": "wide",
4517
+ "width": 1920,
4518
+ "height": 1080,
4519
+ "duration": 14,
4520
+ "video": {
4521
+ "objectKey": "authored-motion/1.0.0/travel-journal/previews/f3c0fb155691217e315928a268bc0af76ac8a1e8792469a869cde35ce9af9baa.mp4",
4522
+ "sha256": "f3c0fb155691217e315928a268bc0af76ac8a1e8792469a869cde35ce9af9baa",
4523
+ "bytes": 55596789,
4524
+ "contentType": "video/mp4"
4525
+ },
4526
+ "poster": {
4527
+ "objectKey": "authored-motion/1.0.0/travel-journal/previews/258cb8ec8949ee778524f00a2873185bfacfbb19d1a821b8e0e21bc7eca46db3.jpg",
4528
+ "sha256": "258cb8ec8949ee778524f00a2873185bfacfbb19d1a821b8e0e21bc7eca46db3",
4529
+ "bytes": 319106,
4530
+ "contentType": "image/jpeg"
4531
+ }
4532
+ },
4533
+ {
4534
+ "ratio": "square",
4535
+ "width": 1080,
4536
+ "height": 1080,
4537
+ "duration": 14,
4538
+ "video": {
4539
+ "objectKey": "authored-motion/1.0.0/travel-journal/previews/976b10af9149cfd4d8b6cc34571eb0e95054ef01d5bfb81c7f0248273603fd4a.mp4",
4540
+ "sha256": "976b10af9149cfd4d8b6cc34571eb0e95054ef01d5bfb81c7f0248273603fd4a",
4541
+ "bytes": 33053492,
4542
+ "contentType": "video/mp4"
4543
+ },
4544
+ "poster": {
4545
+ "objectKey": "authored-motion/1.0.0/travel-journal/previews/2a52f2f0720456e8b7ff53d96616c56dc49b5e1781650903306074180a191c38.jpg",
4546
+ "sha256": "2a52f2f0720456e8b7ff53d96616c56dc49b5e1781650903306074180a191c38",
4547
+ "bytes": 219759,
4548
+ "contentType": "image/jpeg"
4549
+ }
4550
+ },
4551
+ {
4552
+ "ratio": "tall",
4553
+ "width": 1080,
4554
+ "height": 1920,
4555
+ "duration": 14,
4556
+ "video": {
4557
+ "objectKey": "authored-motion/1.0.0/travel-journal/previews/9ad3462e0de6ec81a3b48e10caf196fc8f64b633a4d3a318dc8791b6d1d6c63a.mp4",
4558
+ "sha256": "9ad3462e0de6ec81a3b48e10caf196fc8f64b633a4d3a318dc8791b6d1d6c63a",
4559
+ "bytes": 53803542,
4560
+ "contentType": "video/mp4"
4561
+ },
4562
+ "poster": {
4563
+ "objectKey": "authored-motion/1.0.0/travel-journal/previews/f3027a8d8a78668fe997e1c87521f4bc723d6dcbd46ba8d4d050ede358cb3f71.jpg",
4564
+ "sha256": "f3027a8d8a78668fe997e1c87521f4bc723d6dcbd46ba8d4d050ede358cb3f71",
4565
+ "bytes": 325659,
4566
+ "contentType": "image/jpeg"
4567
+ }
4568
+ }
4569
+ ],
4570
+ "qa": {
4571
+ "status": "source-and-preview-hashes-verified",
4572
+ "previewCount": 3,
4573
+ "artisticApproval": "not-certified",
4574
+ "audioListened": false,
4575
+ "transferCertified": false
4576
+ },
4577
+ "restrictions": [
4578
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
4579
+ "Scene-specific data/count/layout limits are documented in the selected card.",
4580
+ "Opaque output only; no alpha export.",
4581
+ "Voice/music requires timeScale=1 until explicitly retimed.",
4582
+ "Inspect frame boundaries, typography and final audio before delivery."
4583
+ ]
4584
+ },
4585
+ {
4586
+ "id": "lower-third",
4587
+ "version": "1.0.0",
4588
+ "title": "R\xF3tulo de nombre",
4589
+ "mechanism": "hairline-name-super",
4590
+ "intent": "R\xF3tulo para entrevistas y podcasts: nombre, cargo y tema que se relevan sobre una l\xEDnea dorada.",
4591
+ "niche": "creadores",
4592
+ "style": "marca",
4593
+ "duration": 9,
4594
+ "runtime": "canvas",
4595
+ "supportedRatios": [
4596
+ "16:9",
4597
+ "1:1",
4598
+ "9:16"
4599
+ ],
4600
+ "sourceSha256": "fac06c5fb1c58a19538e04835de1eda96764c7e24b79f17e11249bad3f7b971c",
4601
+ "cardSha256": "f0c0f11bf6fc8f75d92050b12842c66ffbf7746db575be06f3232bb15bf5fcc5",
4602
+ "card": '# lower-third \xB7 Nombre y tema sobre la misma l\xEDnea\n\n**Mecanismo.** Una l\xEDnea fina (`accent`) se dibuja bajo el hablante; el nombre sube a trav\xE9s de ella letra por letra (recortado por encima de la l\xEDnea), el rol cae debajo en may\xFAsculas espaciadas y un mosaico con monograma gira en la ra\xEDz de la l\xEDnea. La misma l\xEDnea hace el relevo: se alarga, el nombre se desliza fuera a lo largo de ella, el mosaico gira a unas comillas y el tema entra donde estaba el nombre (misma l\xEDnea base, calculada con el descendente real del texto). Al final todo se retrae hacia el mosaico, que se pliega: el cuadro vuelve a ser la cabeza parlante limpia. Nunca toca el tercio superior.\n\n**\xDAsala para.** Entrevistas, p\xF3dcast en video, testimonios, webinars, cursos (instructor y luego tema del m\xF3dulo), conferencias. **No la uses para.** Nombres o temas de 2 renglones (no hay wrap: se encogen), dos personas a la vez, metraje con texto propio en la parte baja, y todav\xEDa no para exportar sobre metraje real (ver Limitaciones).\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| line | 0.35 | Velo inferior (\u22120.2\u2026+0.6); la l\xEDnea crece 0.7 s hasta `len1` (= max(nombre, rol) + 46u); el mosaico gira con las iniciales (+0.15\u2026+0.65); un destello corre en la punta mientras la l\xEDnea se mueve. |\n| name | 0.8 | Las letras suben desde debajo de la l\xEDnea, una cada 0.034 s (0.6 s cada una). |\n| role | 1.5 | El rol (MAY\xDAS, `heavy`, `accent`, tracking 0.26) cae debajo de la l\xEDnea (0.55 s). |\n| handoff | 4.2 | El rol se retrae (0.32 s); la l\xEDnea se alarga a `len2` (+0.2\u2026+0.75); el nombre se desliza a la derecha y se apaga 60 % (+0.45\u2026+1.05). |\n| topic | 5.35 | El mosaico gira a comillas sobre `accent` (\u22120.45\u2026\u22120.05); `topicLabel` (it\xE1lica, `accent`) + `topic` entran desde el mosaico (0.7 s). |\n| out | 7.45 | El tema se retrae (0.45 s); la l\xEDnea vuelve al mosaico (+0.35\u2026+0.9); el mosaico se pliega (+0.75\u2026+1.05); el velo se va (+0.6\u2026+1.3). |\n| end | 9 | ~0.5 s de cuadro limpio. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand, duration, fps, timeScale | kit `noir`, 9 s | it\xE1lica real solo en kits con Instrument Serif o Baskerville (`noir`, `press`, `sage`, `santi`); en los dem\xE1s el navegador la sintetiza |\n| background | `"placeholder"`: sustituto procedural desenfocado (figura, micr\xF3fono, empuje lento) + velo + grano + r\xF3tulo; `"none"`: solo gr\xE1ficos sobre `bg` s\xF3lido, mosaico opaco | \u2014 |\n| copy.name | `display` 78 / 66 / 72u; se encoge a 92 % del ancho seguro (m\xEDn. 30u) | 1 l\xEDnea |\n| copy.role | MAY\xDAS; se encoge (m\xEDn. 11u) | 1 l\xEDnea |\n| copy.monogram | letras del mosaico; vac\xEDo \u2192 iniciales de las 2 primeras palabras de `name` | 2 car. (sin fit) |\n| copy.topicLabel / topic | mismo tama\xF1o (\u2248 0.9\xD7 el nombre); se encogen juntos al ancho seguro | 1 l\xEDnea |\n| copy.placeholder | r\xF3tulo abajo-derecha (solo con `"placeholder"`) | \u2014 |\n| beats | ver tabla | deja \u2265 2.5 s entre `name` y `handoff` para leer el nombre |\n| sound.cues | 7 cues | \u2014 |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- Nombre, rol, monograma, tema y su etiqueta; `background`.\n- Beats con la voz: `name` cuando empieza a hablar, `handoff`/`topic` cuando anuncia el tema, `out` antes del siguiente corte.\n- Roles: `accent` (l\xEDnea, rol, etiqueta de tema, borde del mosaico y su cara de comillas), `accent2` (iniciales, centro del degradado de la l\xEDnea, destello), `surface` (mosaico), `ink` (nombre, tema), `bg` (velo, comillas), `muted` (r\xF3tulo). Fuentes: `display`, `heavy`, `mono`.\n\n## Qu\xE9 requiere tocar c\xF3digo\n- Salida con alfa o ProRes 4444 en `tools/render.mjs` para componer sobre metraje.\n- Video real de fondo (el runtime solo carga im\xE1genes), logo en lugar de monograma, dos renglones, versi\xF3n alineada a la derecha.\n\n## Formatos\n- 16:9: inicio al 7.5 % W, l\xEDnea al 80.5 % H, mosaico 88u; figura del sustituto al 64 % W.\n- 1:1: 8 % W, l\xEDnea al 79 % H, mosaico 80u.\n- 9:16: 8.5 % W, l\xEDnea al 61.5 % H (por encima de la zona de subt\xEDtulos), mosaico 84u; el velo es una banda que no llega al borde inferior.\n- El ancho seguro termina al mismo margen por la derecha.\n\n## Sonido\nwhoosh-short + snap (+0.62) al dibujar la l\xEDnea; tick en `role`; whoosh-short en `handoff` (+0.35); snap + whoosh-short en `topic`; whoosh-short al salir. Funciona muda.\n\n## Limitaciones\n- El render actual produce H.264 opaco: con `background: "none"` el resultado es gr\xE1fico sobre `bg` s\xF3lido, no transparente (pendiente en `INTEGRATION.md`).\n- El sustituto no es una persona ni una foto: sirve para revisar legibilidad y colocaci\xF3n; mantener su r\xF3tulo.\n- En `noir` el texto es claro: sobre metraje claro en la parte baja, el velo (m\xE1x. 62 % de `bg`) puede no bastar.\n- Sin wrap: bajo los m\xEDnimos (30u / 11u) el texto se sale del ancho seguro.\n\n## Adaptar a otro tema\n- Noticias (`theme: press`): `name "Mariana Ruiz"`, `role "Economista \xB7 Invitada"`, `topicLabel "Tema:"`, `topic "\xBFViene una recesi\xF3n?"`.\n- Curso online (`theme: santi`): `name "Andr\xE9s Vela"`, `role "Instructor \xB7 M\xF3dulo 3"`, `monogram "AV"`, `topicLabel "Hoy:"`, `topic "Automatiza tu bandeja"`.\n- Salud (`theme: sage`): `name "Dra. Elena R\xEDos"`, `role "Cardi\xF3loga invitada"`, `monogram "ER"`, `topicLabel "Tema:"`, `topic "Hipertensi\xF3n silenciosa"`.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Sin media (no hay `params.media`): la figura de fondo es un sustituto procedural, no una foto ni una persona real.\n',
4603
+ "bundle": {
4604
+ "objectKey": "authored-motion/1.0.0/lower-third/e1a0ae8bdee6989a57263e2bb95936371645d1ddf8e203b767221f964708e3ee.tar.gz",
4605
+ "sha256": "e1a0ae8bdee6989a57263e2bb95936371645d1ddf8e203b767221f964708e3ee",
4606
+ "bytes": 2936742,
4607
+ "fileCount": 58,
4608
+ "contentType": "application/gzip"
4609
+ },
4610
+ "previews": [
4611
+ {
4612
+ "ratio": "wide",
4613
+ "width": 1920,
4614
+ "height": 1080,
4615
+ "duration": 9,
4616
+ "video": {
4617
+ "objectKey": "authored-motion/1.0.0/lower-third/previews/761405053ea49b0555b1f2aea7ff270dae1b5642244ebe35a447ba10f32302ab.mp4",
4618
+ "sha256": "761405053ea49b0555b1f2aea7ff270dae1b5642244ebe35a447ba10f32302ab",
4619
+ "bytes": 86568079,
4620
+ "contentType": "video/mp4"
4621
+ },
4622
+ "poster": {
4623
+ "objectKey": "authored-motion/1.0.0/lower-third/previews/2a173f59e8a20c95dd46b8d87c008f5b8953f2350e75bb29f8a880ae66f97fae.jpg",
4624
+ "sha256": "2a173f59e8a20c95dd46b8d87c008f5b8953f2350e75bb29f8a880ae66f97fae",
4625
+ "bytes": 273563,
4626
+ "contentType": "image/jpeg"
4627
+ }
4628
+ },
4629
+ {
4630
+ "ratio": "square",
4631
+ "width": 1080,
4632
+ "height": 1080,
4633
+ "duration": 9,
4634
+ "video": {
4635
+ "objectKey": "authored-motion/1.0.0/lower-third/previews/dc8ccc1c1deb002d3b62aaf5291462b9f7f938461055d8e1b50071124402b1c4.mp4",
4636
+ "sha256": "dc8ccc1c1deb002d3b62aaf5291462b9f7f938461055d8e1b50071124402b1c4",
4637
+ "bytes": 48517883,
4638
+ "contentType": "video/mp4"
4639
+ },
4640
+ "poster": {
4641
+ "objectKey": "authored-motion/1.0.0/lower-third/previews/5d12e737cedd8d74755e9ada7172db8cb90123abe3002b10e180dbd398a0b576.jpg",
4642
+ "sha256": "5d12e737cedd8d74755e9ada7172db8cb90123abe3002b10e180dbd398a0b576",
4643
+ "bytes": 158818,
4644
+ "contentType": "image/jpeg"
4645
+ }
4646
+ },
4647
+ {
4648
+ "ratio": "tall",
4649
+ "width": 1080,
4650
+ "height": 1920,
4651
+ "duration": 9,
4652
+ "video": {
4653
+ "objectKey": "authored-motion/1.0.0/lower-third/previews/79480c0b40a3ab1d52d7dd0f35b97c215c3c3f3c201d92fc58d79eece5ce2021.mp4",
4654
+ "sha256": "79480c0b40a3ab1d52d7dd0f35b97c215c3c3f3c201d92fc58d79eece5ce2021",
4655
+ "bytes": 86095677,
4656
+ "contentType": "video/mp4"
4657
+ },
4658
+ "poster": {
4659
+ "objectKey": "authored-motion/1.0.0/lower-third/previews/df8454b4b6198ddad8988aceb7753ef2ddce24b4b3b6f4ad4e7dd7d7dea65599.jpg",
4660
+ "sha256": "df8454b4b6198ddad8988aceb7753ef2ddce24b4b3b6f4ad4e7dd7d7dea65599",
4661
+ "bytes": 271828,
4662
+ "contentType": "image/jpeg"
4663
+ }
4664
+ }
4665
+ ],
4666
+ "qa": {
4667
+ "status": "source-and-preview-hashes-verified",
4668
+ "previewCount": 3,
4669
+ "artisticApproval": "not-certified",
4670
+ "audioListened": false,
4671
+ "transferCertified": false
4672
+ },
4673
+ "restrictions": [
4674
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
4675
+ "Scene-specific data/count/layout limits are documented in the selected card.",
4676
+ "Opaque output only; no alpha export.",
4677
+ "Voice/music requires timeScale=1 until explicitly retimed.",
4678
+ "Inspect frame boundaries, typography and final audio before delivery."
4679
+ ]
4680
+ },
4681
+ {
4682
+ "id": "cinematic-title",
4683
+ "version": "1.0.0",
4684
+ "title": "T\xEDtulo de documental",
4685
+ "mechanism": "light-sweep-title",
4686
+ "intent": "Intro de pel\xEDcula o marca: barras de cine y un haz de luz que escribe el t\xEDtulo.",
4687
+ "niche": "documental",
4688
+ "style": "cine",
4689
+ "duration": 10,
4690
+ "runtime": "canvas",
4691
+ "supportedRatios": [
4692
+ "16:9",
4693
+ "1:1",
4694
+ "9:16"
4695
+ ],
4696
+ "sourceSha256": "12d9f7493803765641def0c80f55dd9c83cf8f030da434ad21c9019bfd9d8983",
4697
+ "cardSha256": "71f35859923b574f153b09a8c953d8d6b0df395a7658d6b1a205d6b373a9d17f",
4698
+ "card": '# cinematic-title \xB7 T\xEDtulo escrito por la luz\n\n**Mecanismo.** El cuadro se cierra a formato scope con barras; un solo haz anam\xF3rfico (raya + bloom aditivos) cruza el aire oscuro de izquierda a derecha y escribe el t\xEDtulo: las letras existen solo donde el haz ya pas\xF3 y se quedan encendidas; en la pasada se calientan a dorado-blanco y luego se enfr\xEDan. El polvo suspendido brilla al paso del haz y la sala conserva un resplandor. Siguen una regla y el subt\xEDtulo; al final la imagen funde y las barras se abren a cuadro completo en negro: punto de corte limpio para el primer plano. Todo el cuadro tiene vaiv\xE9n de proyector, empuje lento, vi\xF1eta, grano y parpadeo.\n\n**\xDAsala para.** Intro de documental o mini-documental de marca (historia de un negocio familiar), tr\xE1iler de curso, apertura de caso de estudio, cap\xEDtulo de una serie de YouTube. **No la uses para.** T\xEDtulos largos o de 2 l\xEDneas (se encogen hasta 40u), kits claros (todo es aditivo: sobre fondo claro no se ve), mensajes con datos, un t\xEDtulo que deba quedarse en pantalla (el final es negro).\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| bars | 0.3 | Las barras cierran al `gate` en 1.25 s; el polvo aparece 0.1\u20131.2. Empuje lento de 3.5 % entre 1.2 y 9.5 s. |\n| beam | 1.7 | El haz cruza de \u221222 % a 122 % W en 2.7 s (entra en 0.5 s, se va de +2.2 a +2.75); las letras se encienden con borde suave (7.5 % W) detr\xE1s de \xE9l; pasada caliente con blur; el aire queda iluminado de +0.6 a +2.6. |\n| subtitle | 4.35 | Regla desde el centro (\u22120.15\u2026+0.9); subt\xEDtulo con tracking que se asienta (1.3 s). |\n| out | 7.9 | Fundido de la imagen (1 s); las barras se abren de +0.25 a +1.25. |\n| end | 10 | Imagen fundida a `bg` (queda grano) desde 8.9 s; barras abiertas a 9.15 s. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand, duration, fps, timeScale | kit `cinema`, 10 s | kit oscuro obligatorio |\n| copy.title | 1 l\xEDnea, fit al 62 / 78 / 80 % del ancho, m\xE1x. 190 / 150 / 160u, m\xEDn. 40u | m\xE1s largo = m\xE1s chico (fit), nunca wrap |\n| copy.subtitle | 1 l\xEDnea, 28 / 25 / 28u; se encoge si pasa del 88 % W | \u2014 |\n| style.titleTracking / subtitleTracking / subtitleUpper | tracking (em) y MAY\xDAS del subt\xEDtulo | \u2014 |\n| gate.wide / square / tall | proporci\xF3n de la franja visible | 9:16 cierra a 1:1 a prop\xF3sito: un 2.39 dejar\xEDa 452 px de imagen |\n| dust | n\xFAmero de part\xEDculas (sembradas) | 90; m\xE1s = m\xE1s costo por cuadro |\n| beats | ver tabla | `beam` + 2.75 < `subtitle` para que el t\xEDtulo est\xE9 completo |\n| sound.cues | 6 cues | \u2014 |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- T\xEDtulo, subt\xEDtulo, tracking, gate por formato, cantidad de polvo, beats.\n- Beats con la m\xFAsica: `beam` en el swell, `out` donde entra el primer plano.\n- Roles: `bg` (barras, aire, vi\xF1eta, negro final), `surface` (aire medio), `ink` (n\xFAcleo del haz, letras), `accent` (haz, bloom, resplandor, polvo, regla), `muted` (mezcla del subt\xEDtulo). Fuentes: `display` (t\xEDtulo), `text` (subt\xEDtulo).\n\n## Qu\xE9 requiere tocar c\xF3digo\n- Direcci\xF3n o altura del haz (siempre a media altura del t\xEDtulo, de izquierda a derecha).\n- T\xEDtulo de 2 l\xEDneas, logo en lugar de texto, metraje de fondo, sin barras, t\xEDtulo que se quede al final.\n\n## Formatos\n- 16:9: gate 2.39 \u2192 franja de 803 px (barras de 138 px); t\xEDtulo \u2264 62 % W.\n- 1:1: gate 2.39 \u2192 franja de 452 px (barras de 314 px); t\xEDtulo \u2264 78 % W, 150u.\n- 9:16: gate 1:1 \u2192 franja de 1080 px (barras de 420 px); t\xEDtulo \u2264 80 % W, 160u.\n\n## Sonido\nwhoosh + thud (+1.2, barras cerradas) en `bars`; swell + riser (+0.5) en `beam`; chime suave en `subtitle`; whoosh en `out`. Funciona muda.\n\n## Limitaciones\n- El vaiv\xE9n, el grano y el parpadeo (solo oscurece, \xB13 %) se sortean por cuadro: el look cambia si cambias `fps`.\n- Costo: capa offscreen a pantalla completa y `blur` por cuadro mientras pasa el haz.\n- El polvo es gen\xE9rico (c\xEDrculos aditivos), no harina ni humo dibujados.\n- El t\xEDtulo completo se ve de ~4.4 a 7.9 s; despu\xE9s solo queda negro.\n\n## Adaptar a otro tema\n- Herrer\xEDa familiar (`theme: noir`): `title "LA FORJA"`, `subtitle "Cuarenta a\xF1os de herrer\xEDa"`, `dust 120`.\n- Curso (`theme: cinema`): `title "M\xD3DULO 01"`, `subtitle "Fundamentos de automatizaci\xF3n"`, `subtitleTracking 0.3`, `dust 60`.\n- Caso de estudio log\xEDstico (`theme: blueprint`): `title "RUTA 30"`, `subtitle "De 3 a 30 camiones en un a\xF1o"`, `gate.tall 1.25`.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Sin media (no hay `params.media`): haz, polvo y textura se generan con c\xF3digo.\n',
4699
+ "bundle": {
4700
+ "objectKey": "authored-motion/1.0.0/cinematic-title/446bdf7e57cd0bb56763f2a8a1da7eca1a3171d3b8d015bf53065d1cd681b149.tar.gz",
4701
+ "sha256": "446bdf7e57cd0bb56763f2a8a1da7eca1a3171d3b8d015bf53065d1cd681b149",
4702
+ "bytes": 2935403,
4703
+ "fileCount": 58,
4704
+ "contentType": "application/gzip"
4705
+ },
4706
+ "previews": [
4707
+ {
4708
+ "ratio": "wide",
4709
+ "width": 1920,
4710
+ "height": 1080,
4711
+ "duration": 10,
4712
+ "video": {
4713
+ "objectKey": "authored-motion/1.0.0/cinematic-title/previews/b25521317fcc3b1d7b81b8a837cc68cc0e35554b19cc90da2ce767c1b66a0517.mp4",
4714
+ "sha256": "b25521317fcc3b1d7b81b8a837cc68cc0e35554b19cc90da2ce767c1b66a0517",
4715
+ "bytes": 98934082,
4716
+ "contentType": "video/mp4"
4717
+ },
4718
+ "poster": {
4719
+ "objectKey": "authored-motion/1.0.0/cinematic-title/previews/736fad8279649ae1869a2ff94d29d3f702a4d9f23eab5aee302831e2b4aaa096.jpg",
4720
+ "sha256": "736fad8279649ae1869a2ff94d29d3f702a4d9f23eab5aee302831e2b4aaa096",
4721
+ "bytes": 330754,
4722
+ "contentType": "image/jpeg"
4723
+ }
4724
+ },
4725
+ {
4726
+ "ratio": "square",
4727
+ "width": 1080,
4728
+ "height": 1080,
4729
+ "duration": 10,
4730
+ "video": {
4731
+ "objectKey": "authored-motion/1.0.0/cinematic-title/previews/4f5129530cd45d09172d48104bebbe84dd1cb706b05364e2dd388e82ae8e0174.mp4",
4732
+ "sha256": "4f5129530cd45d09172d48104bebbe84dd1cb706b05364e2dd388e82ae8e0174",
4733
+ "bytes": 38086162,
4734
+ "contentType": "video/mp4"
4735
+ },
4736
+ "poster": {
4737
+ "objectKey": "authored-motion/1.0.0/cinematic-title/previews/7ad6615841f46df441e8fb26fce4622972c2a79cd86dae49b5f3eeff796c897c.jpg",
4738
+ "sha256": "7ad6615841f46df441e8fb26fce4622972c2a79cd86dae49b5f3eeff796c897c",
4739
+ "bytes": 147686,
4740
+ "contentType": "image/jpeg"
4741
+ }
4742
+ },
4743
+ {
4744
+ "ratio": "tall",
4745
+ "width": 1080,
4746
+ "height": 1920,
4747
+ "duration": 10,
4748
+ "video": {
4749
+ "objectKey": "authored-motion/1.0.0/cinematic-title/previews/2fc21949c7260ad14e00958343c024e7ebbc413f3b6a32e064ab18136f12b597.mp4",
4750
+ "sha256": "2fc21949c7260ad14e00958343c024e7ebbc413f3b6a32e064ab18136f12b597",
4751
+ "bytes": 81689939,
4752
+ "contentType": "video/mp4"
4753
+ },
4754
+ "poster": {
4755
+ "objectKey": "authored-motion/1.0.0/cinematic-title/previews/4a358711bf24283469cc31c999128cdb28cdc3ccd6370a75889c36ce527490a7.jpg",
4756
+ "sha256": "4a358711bf24283469cc31c999128cdb28cdc3ccd6370a75889c36ce527490a7",
4757
+ "bytes": 289948,
4758
+ "contentType": "image/jpeg"
4759
+ }
4760
+ }
4761
+ ],
4762
+ "qa": {
4763
+ "status": "source-and-preview-hashes-verified",
4764
+ "previewCount": 3,
4765
+ "artisticApproval": "not-certified",
4766
+ "audioListened": false,
4767
+ "transferCertified": false
4768
+ },
4769
+ "restrictions": [
4770
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
4771
+ "Scene-specific data/count/layout limits are documented in the selected card.",
4772
+ "Opaque output only; no alpha export.",
4773
+ "Voice/music requires timeScale=1 until explicitly retimed.",
4774
+ "Inspect frame boundaries, typography and final audio before delivery."
4775
+ ]
4776
+ },
4777
+ {
4778
+ "id": "spotlight-roll",
4779
+ "version": "1.0.0",
4780
+ "title": "Ninguno empez\xF3 listo",
4781
+ "mechanism": "archive-spotlight-cut-grammar",
4782
+ "intent": "Testimonios o casos: persona a color sobre mundo gris, logro al lado de la cara, montaje y golpe final.",
4783
+ "niche": "creadores",
4784
+ "style": "foto",
4785
+ "duration": 16.5,
4786
+ "runtime": "canvas",
4787
+ "supportedRatios": [
4788
+ "16:9",
4789
+ "1:1",
4790
+ "9:16"
4791
+ ],
4792
+ "sourceSha256": "be9c8e74023ea3a4257e3c5cd1eb9b955820ca51468e765d5f4500ba84947f00",
4793
+ "cardSha256": "03d00cc32697277df864cf6c189362605b76c806a98c91ac36abbf2815f522fa",
4794
+ "card": '# spotlight-roll \xB7 Foco de archivo con palabras junto a la cara\n\n**Mecanismo.** Tres cap\xEDtulos de retrato. En cada uno el mundo de la persona pasa a gris (desaturado, contraste +15 %, brillo 62 %, blur 3 px) y solo ella conserva el color dentro de un foco suave con halo c\xE1lido; su logro se escribe a los dos lados de la cara en dos golpes secos (`left`, `right`) con un bloom blanco que decae, y debajo aparecen nombre y un dato honesto. El movimiento va escalonado a `stepFps` (look de edici\xF3n a mano). Jerarqu\xEDa de cortes: 2 cuadros en negativo punt\xFAan dentro del cap\xEDtulo; una placa de grano de 5 cuadros marca el cambio de cap\xEDtulo. Despu\xE9s, un montaje de cortes de 0.4 s (cada uno abre con 1 cuadro en negativo) mientras una frase se sigue construyendo palabra por palabra encima de los cortes; cierra una palabra gigante 2 cuadros a alto de cuadro que se asienta peque\xF1a sobre negro.\n\n**\xDAsala para.** Testimonios de alumnos o clientes (escuela, bootcamp, coach, gimnasio), incubadoras y comunidades de emprendedores, reclutamiento ("ellos empezaron aqu\xED"), campa\xF1as con 3 historias cortas. **No la uses para.** Personas reales sin consentimiento o logros sin verificar (el formato los presenta como hechos); audiencias fotosensibles sin revisi\xF3n (ver Limitaciones); m\xE1s o menos de 3 historias; fotos sin una cara clara.\n\n## L\xEDnea de tiempo\n| beat | s | qu\xE9 pasa |\n|---|---|---|\n| c1 | 0.15 | Cap\xEDtulo 1 (hasta c2, 3.8 s): retrato gris, el color entra en el foco +0.15\u2026+0.55; `left` a +0.5 y `right` a +0.8 (corte seco + bloom); `name` +1.2\u2026+1.5; `note` +1.6\u2026+1.9; 2 cuadros en negativo a +1.6; empuje de 2 %/s (tope a 3.5 s). |\n| c2 | 3.95 | Placa de grano de 5 cuadros justo antes (\u22120.17 s); cap\xEDtulo 2 igual. |\n| c3 | 7.75 | Placa + cap\xEDtulo 3. |\n| montage | 11.55 | Placa antes; cortes cada 0.4 s ciclando `closingMedia` (color pleno, sin palabras), 1 cuadro en negativo al inicio de cada corte; `closingWords`: la 1.\xAA desde el primer corte, luego una cada 6 cuadros desde +0.3 (+0.5, +0.7), ancladas a la izquierda. |\n| punch | 14.15 | 2 cuadros: campo `ink` a cuadro completo con `punch` en negro a alto de cuadro; luego la palabra peque\xF1a sobre negro con brillo `accent` que decae. |\n| end | 16.5 | Hold ~2.3 s. |\n\n## Par\xE1metros\n| clave | efecto | l\xEDmites |\n|---|---|---|\n| theme / brand, duration, fps, timeScale | kit `cinema`, 16.5 s | fondo `#000`, negativos, placa gris `#6d6a66` y bloom blanco fijos; `timeScale` \u2260 1 rompe la puntuaci\xF3n (Limitaciones) |\n| stepFps | cadencia del movimiento escalonado | 12 \u2192 saltos de 2\u20133 cuadros a 30 fps |\n| media | `{clave: ruta}` | fotos verticales 1080\xD71920; clave inexistente \u2192 cuadro negro |\n| people[] | `{media, face: [x, y] 0\u20131, radius, name, left, right, note}`; `radius` = fracci\xF3n del ancho mostrado de la foto | se usan exactamente los 3 primeros (c1\u2013c3); con menos la escena falla. `left`/`right` se encogen para caber junto a la cara, sin m\xEDnimo; `name` (mono MAY\xDAS) y `note` (display it\xE1lica) en 1 l\xEDnea sin maxW |\n| closingMedia | claves del montaje, en orden | clave sin persona \u2192 foco por defecto (cara [0.48, 0.33], radio 0.34) |\n| closingWords | palabras de la frase final | sin wrap ni fit: en 1:1 y 9:16 una frase larga se sale por la derecha |\n| punch | palabra final | 1 palabra corta |\n| note | aviso abajo-derecha (clave de primer nivel, no `copy`) | mantener "Personajes ficticios" |\n| beats | c1, c2, c3, montage, punch, end | la duraci\xF3n del montaje es `punch \u2212 montage` |\n| sound | `cues` vac\xEDo: todo sale de `cues(S)` | \u2014 |\n\n## Qu\xE9 cambiar sin tocar c\xF3digo\n- Fotos, encuadre de cara y foco por persona, logros, nombres, notas, frase y palabra final.\n- Beats con la voz: cada `c*` al presentar a la persona, `punch` en la \xFAltima palabra dicha.\n- Roles: `ink` (palabras, nota, campo del punch), `accent` (halo y brillo final). Fuentes: `heavy` (logros, punch), `mono` (nombre), `display` it\xE1lica (nota, frase), `text` (aviso).\n\n## Qu\xE9 requiere tocar c\xF3digo\n- N\xFAmero de cap\xEDtulos (`chapterStarts` fijo en 3), video en lugar de fotos.\n- Duraci\xF3n de los cortes del montaje (0.4 s) y frecuencia de negativos (cada 12 cuadros); cambiar el negativo por algo menos agresivo.\n- Posici\xF3n de las palabras (siempre a la altura de la cara) y de la frase.\n\n## Formatos\n- 16:9: zoom 1.25 sobre fotos verticales \u2192 se ve ~25 % del alto de la foto (cara y hombros); logros 110u; frase en 0.82 H desde x 120u.\n- 1:1: zoom 1.05; logros 82u con ~270 px por lado (se encogen); frase 84u. **Sin revisi\xF3n visual**: `reviews/spotlight-roll` solo tiene 16:9 y 9:16.\n- 9:16: zoom 1.0, cara al 42 % H; logros 92u con ~300 px por lado ("40 CLIENTES" se encoge bastante); frase en 0.78 H.\n\n## Sonido\nTodo generado: snap a +0.55 y +0.85 de cada cap\xEDtulo (logros), thud en cada placa (\u22120.12), riser 0.9 s antes del montaje, 6 ticks en el montaje (+0.2 + k\xB70.4), stamp en el punch. Funciona muda.\n\n## Limitaciones\n- **Fotosensibilidad.** Negativos a cuadro completo (2 cuadros por cap\xEDtulo; en el montaje 1 cada 0.4 s \u2248 2.5 destellos/s, cerca del l\xEDmite de 3/s de WCAG 2.3.1), un campo casi blanco de 2 cuadros en el punch y montaje r\xE1pido. Pasarlo por un analizador de destellos (Harding, PEAT) antes de publicar y avisar en la publicaci\xF3n. La frecuencia depende de `fps`: a 60 fps el montaje invierte cada 0.2 s (5/s).\n- `timeScale` \u2260 1 desincroniza la puntuaci\xF3n: negativos, placas y punch comparan el cuadro real (`S.frame`) con beats de autor\xEDa. Para alargar, mueve beats.\n- It\xE1lica sintetizada en `cinema` (Fraunces sin archivo it\xE1lico); en `noir` es real.\n- Si la foto no deja centrar la cara, el recorte se ajusta al borde y la cara puede quedar fuera del foco.\n- Personajes y logros ficticios; no usar con personas reales sin su consentimiento.\n\n## Adaptar a otro tema\n- Bootcamp de programaci\xF3n (`theme: noir`): 3 fotos propias, `left "CONSIGUI\xD3"`, `right "SU 1.ER EMPLEO"`, `note "6 meses despu\xE9s del curso"`, `name "Ana \xB7 generaci\xF3n 3"`; `closingWords ["Nadie","lleg\xF3","sabiendo."]`, `punch "INSCR\xCDBETE."`.\n- Incubadora (`theme: cinema`): `left "VENDI\xD3"`, `right "SU STARTUP"`, `note "con 3 empleados"`; `closingWords ["Todos","empezaron","solos."]`, `punch "APLICA."`.\n- Gimnasio (`theme: sport`): `left "BAJ\xD3"`, `right "12 KILOS"`, `note "en 5 meses, 3 d\xEDas por semana"`; `closingWords ["Ninguno","era","atleta."]`, `punch "EMPIEZA."`.\n\n## Procedencia\nImplementaci\xF3n original para EditorBrain (JS/Canvas). Reinterpreta mecanismos observados en la referencia analizada `style-lab/pilot-db8r` (reel de Instagram de @lightyear.residency, analizado el 2026-10-05): foco de archivo con palabras junto a la cara, placa de grano como cambio de cap\xEDtulo, 1\u20132 cuadros en negativo como corte interno, palabra gigante de 2 cuadros como impacto y frase que se sigue construyendo entre cortes. No se usan medios ni textos del reel. Media: cuadros de metraje de demo ficticio generado con IA por EditorBrain, 1080\xD71920: `media/barista.jpg` \u2190 `demos/ugc-cafe-v3/generation/source.mp4` (sha256 `b8706c11\u20260fe927ff`), `coach.jpg` \u2190 `demos/ugc-story-v2/\u2026` (`48611ef8\u20266d339258`), `asesora.jpg` \u2190 `demos/ugc-professional-v7/\u2026` (`b8f69563\u202643fed79f`), `viajera.jpg` \u2190 `demos/ugc-travel-v6/\u2026` (`0dd42893\u2026f4d47917`); correspondencia por nombre y contenido del cuadro (no hay registro de extracci\xF3n). Personajes ficticios, indicado en cuadro (`note`).\n',
4795
+ "bundle": {
4796
+ "objectKey": "authored-motion/1.0.0/spotlight-roll/e1c6417a4039714870019eae05147a61e7da97792bab1dce954f1cb5162d9e0a.tar.gz",
4797
+ "sha256": "e1c6417a4039714870019eae05147a61e7da97792bab1dce954f1cb5162d9e0a",
4798
+ "bytes": 3399942,
4799
+ "fileCount": 62,
4800
+ "contentType": "application/gzip"
4801
+ },
4802
+ "previews": [
4803
+ {
4804
+ "ratio": "wide",
4805
+ "width": 1920,
4806
+ "height": 1080,
4807
+ "duration": 16.5,
4808
+ "video": {
4809
+ "objectKey": "authored-motion/1.0.0/spotlight-roll/previews/0404820750f58973a5bbba1ed5aeb1a692917d2b65c66852b9f29734bc8b5962.mp4",
4810
+ "sha256": "0404820750f58973a5bbba1ed5aeb1a692917d2b65c66852b9f29734bc8b5962",
4811
+ "bytes": 130250027,
4812
+ "contentType": "video/mp4"
4813
+ },
4814
+ "poster": {
4815
+ "objectKey": "authored-motion/1.0.0/spotlight-roll/previews/ce934c0f8d63719d32e116a5632913f79d9d96b988594e37f7db93189787820d.jpg",
4816
+ "sha256": "ce934c0f8d63719d32e116a5632913f79d9d96b988594e37f7db93189787820d",
4817
+ "bytes": 94008,
4818
+ "contentType": "image/jpeg"
4819
+ }
4820
+ },
4821
+ {
4822
+ "ratio": "square",
4823
+ "width": 1080,
4824
+ "height": 1080,
4825
+ "duration": 16.5,
4826
+ "video": {
4827
+ "objectKey": "authored-motion/1.0.0/spotlight-roll/previews/aea8851db8ee9b2da32b704bf8d020e2cb0b4a143c9cc2d3024aa2f89613b070.mp4",
4828
+ "sha256": "aea8851db8ee9b2da32b704bf8d020e2cb0b4a143c9cc2d3024aa2f89613b070",
4829
+ "bytes": 72527038,
4830
+ "contentType": "video/mp4"
4831
+ },
4832
+ "poster": {
4833
+ "objectKey": "authored-motion/1.0.0/spotlight-roll/previews/9356480621d91b2a5155a6cca7fe589714651a485e24e67bb05d6ec5d215e725.jpg",
4834
+ "sha256": "9356480621d91b2a5155a6cca7fe589714651a485e24e67bb05d6ec5d215e725",
4835
+ "bytes": 42930,
4836
+ "contentType": "image/jpeg"
4837
+ }
4838
+ },
4839
+ {
4840
+ "ratio": "tall",
4841
+ "width": 1080,
4842
+ "height": 1920,
4843
+ "duration": 16.5,
4844
+ "video": {
4845
+ "objectKey": "authored-motion/1.0.0/spotlight-roll/previews/c0cff271e2f886fa12455921fa264c133dc48cf7d2044b241ab518596e164739.mp4",
4846
+ "sha256": "c0cff271e2f886fa12455921fa264c133dc48cf7d2044b241ab518596e164739",
4847
+ "bytes": 149324845,
4848
+ "contentType": "video/mp4"
4849
+ },
4850
+ "poster": {
4851
+ "objectKey": "authored-motion/1.0.0/spotlight-roll/previews/f09ac1aada70a0c2dc462bc10cb19b75fa17ab547cfc56102b7e41e99ee17632.jpg",
4852
+ "sha256": "f09ac1aada70a0c2dc462bc10cb19b75fa17ab547cfc56102b7e41e99ee17632",
4853
+ "bytes": 50372,
4854
+ "contentType": "image/jpeg"
4855
+ }
4856
+ }
4857
+ ],
4858
+ "qa": {
4859
+ "status": "source-and-preview-hashes-verified",
4860
+ "previewCount": 3,
4861
+ "artisticApproval": "not-certified",
4862
+ "audioListened": false,
4863
+ "transferCertified": false
4864
+ },
4865
+ "restrictions": [
4866
+ "Explicit project brand/font/audio binding required; demo identity is not a customer default.",
4867
+ "Scene-specific data/count/layout limits are documented in the selected card.",
4868
+ "Opaque output only; no alpha export.",
4869
+ "Voice/music requires timeScale=1 until explicitly retimed.",
4870
+ "Inspect frame boundaries, typography and final audio before delivery."
4871
+ ]
4872
+ }
4873
+ ];
4874
+
4875
+ // packages/core/src/authored-motion-catalog.ts
4876
+ var AUTHORED_MOTION_CATALOG_VERSION = "1.0.0";
4877
+ var scenes = AUTHORED_MOTION_DATA;
4878
+ var normalized = (value) => value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim().replace(/\s+/g, " ");
4879
+ var aliases = {
4880
+ "word-structure": "dependencia estabilidad palabras tipograf\xEDa",
4881
+ "one-object-order": "interfaz proceso pedido venta compra",
4882
+ "price-anatomy": "precio desglose costo partes",
4883
+ "route-untangle": "ruta mapa orden trayecto",
4884
+ "thread-repurpose": "contenido reutilizar ramificar conexiones",
4885
+ "cash-runway": "flujo caja dinero liquidez",
4886
+ "zoom-niche": "embudo segmentaci\xF3n enfoque zoom",
4887
+ "isotype-reviews": "porcentaje rese\xF1as proporci\xF3n personas",
4888
+ "compound-race": "inversi\xF3n ahorro inter\xE9s compuesto comparaci\xF3n",
4889
+ "batch-week": "calendario agenda tiempo productividad",
4890
+ "exploded-product": "capas materiales corte producto tridimensional",
4891
+ "strike-replace": "corregir tachar reemplazo creencia",
4892
+ "fuse-stat": "gancho apertura urgencia hook",
4893
+ "shape-bridge": "transici\xF3n continuidad transformaci\xF3n",
4894
+ "listing-mosaic": "fotos mosaico inmueble propiedad visita",
4895
+ "phone-pushin": "tel\xE9fono aplicaci\xF3n m\xF3vil interfaz demo",
4896
+ "pricing-slider": "planes tarifa comparador precios",
4897
+ "page-critique": "p\xE1gina cr\xEDtica anotaciones auditor\xEDa",
4898
+ "referral-tree": "referidos \xE1rbol crecimiento red",
4899
+ "brand-seal": "marca sello logo cierre llamada acci\xF3n cta",
4900
+ "chat-closer": "chat asistente ventas prospectos automatizaci\xF3n crm",
4901
+ "night-sales": "notificaciones pedidos noche tienda",
4902
+ "cv-scan": "curr\xEDculum documento lectura atenci\xF3n empleo",
4903
+ "sankey-money": "dinero distribuci\xF3n presupuesto proporciones",
4904
+ "bar-race": "ranking barras gr\xE1fica evoluci\xF3n clasificaci\xF3n",
4905
+ "waffle-hours": "tiempo horas automatizaci\xF3n recursos",
4906
+ "clay-store": "pedido log\xEDstica entrega diorama tridimensional",
4907
+ "blueprint-build": "plano construcci\xF3n arquitectura costo",
4908
+ "heartbeat": "salud pulso frecuencia se\xF1al tendencia",
4909
+ "travel-journal": "viaje itinerario diario ruta mapa turismo",
4910
+ "lower-third": "nombre entrevista podcast r\xF3tulo identificaci\xF3n",
4911
+ "cinematic-title": "cine documental t\xEDtulo luz apertura",
4912
+ "spotlight-roll": "testimonio archivo persona foto foco montaje"
4913
+ };
4914
+ function listAuthoredMotionScenes(options = {}) {
4915
+ if (!options || typeof options !== "object" || Array.isArray(options) || Object.keys(options).some((key) => !["query", "limit", "after"].includes(key))) throw new Error("Authored motion search options are invalid.");
4916
+ const { query = "", limit = 6, after } = options;
4917
+ if (typeof query !== "string" || query.length > 200 || /[\x00-\x1f\x7f]/.test(query)) throw new Error("Authored motion query must be a string of at most 200 characters without control characters.");
4918
+ if (!Number.isInteger(limit) || limit < 1 || limit > 20) throw new Error("Authored motion limit must be an integer from 1 to 20.");
4919
+ if (after !== void 0 && (typeof after !== "string" || after.length > 2e3)) throw new Error("Invalid authored motion cursor.");
4920
+ const search = normalized(query), terms = search.split(" ").filter(Boolean);
4921
+ const candidates = scenes.filter((scene) => {
4922
+ const text5 = normalized([scene.id, scene.title, scene.mechanism, scene.intent, scene.niche, scene.style, aliases[scene.id] || ""].join(" "));
4923
+ return terms.every((term) => text5.includes(term));
4924
+ });
4925
+ let start2 = 0;
4926
+ if (after !== void 0) {
4927
+ const [version, encoded, id2, extra] = after.split("|");
4928
+ if (version !== AUTHORED_MOTION_CATALOG_VERSION || encoded !== encodeURIComponent(search) || !id2 || extra !== void 0) throw new Error("Cursor does not match this authored motion query or version.");
4929
+ start2 = candidates.findIndex((scene) => scene.id === id2) + 1;
4930
+ if (start2 === 0) throw new Error("Cursor scene is not part of this authored motion query.");
4931
+ }
4932
+ const page = candidates.slice(start2, start2 + limit);
4933
+ return {
4934
+ version: AUTHORED_MOTION_CATALOG_VERSION,
4935
+ items: page.map(({ id: id2, version, title, mechanism, intent, niche, style, duration, runtime, supportedRatios }) => ({ id: id2, version, title, mechanism, intent, niche, style, duration, runtime, supportedRatios: [...supportedRatios] })),
4936
+ nextCursor: start2 + page.length < candidates.length ? `${AUTHORED_MOTION_CATALOG_VERSION}|${encodeURIComponent(search)}|${page[page.length - 1].id}` : null,
4937
+ total: candidates.length
4938
+ };
4939
+ }
4940
+ function getAuthoredMotionCard(id2, version) {
4941
+ const match = scenes.find((scene) => scene.id === id2 && (version === void 0 || version === scene.version));
4942
+ if (!match) return void 0;
4943
+ const { card, cardSha256, sourceSha256, bundle, qa, restrictions } = match;
4944
+ return structuredClone({ id: match.id, version: match.version, card, cardSha256, sourceSha256, bundle, qa, restrictions });
4945
+ }
4946
+
1703
4947
  // packages/core/src/index.ts
1704
4948
  var SCHEMA_VERSION = 1;
1705
4949
  var MAX_DOCUMENT_BYTES = 128 * 1024;
@@ -1713,6 +4957,7 @@ var ValidationError = class extends Error {
1713
4957
  };
1714
4958
  var identifier3 = /^[a-z0-9][a-z0-9_-]{0,79}$/;
1715
4959
  var tokenName = /^[a-z][a-z0-9_.-]{0,79}$/;
4960
+ var tokenNameHelp = "Use 1\u201380 characters: start with a lowercase letter, then lowercase letters, digits, _, . or - (for example font_family). __proto__, constructor and prototype are reserved. Keys are flat semantic names, not CSS property names.";
1716
4961
  var own2 = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
1717
4962
  var object3 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
1718
4963
  var nonempty2 = (value) => typeof value === "string" && value.trim().length > 0;
@@ -1736,7 +4981,7 @@ function checkRules(value, add) {
1736
4981
  return;
1737
4982
  }
1738
4983
  for (const [key, rule] of Object.entries(value)) {
1739
- if (!tokenName.test(key) || ["__proto__", "constructor", "prototype"].includes(key)) add(`Invalid semantic token ${key}`);
4984
+ if (!tokenName.test(key) || ["__proto__", "constructor", "prototype"].includes(key)) add(`Invalid semantic token ${key}. ${tokenNameHelp}`);
1740
4985
  if (!object3(rule) || !tokenValue(rule.value) || !["required", "preferred", "example"].includes(String(rule.strength))) add(`Token ${key} requires value and strength required/preferred/example`);
1741
4986
  }
1742
4987
  }
@@ -1832,7 +5077,7 @@ function validateDocument(path, content) {
1832
5077
  if (doc.overrides !== void 0) {
1833
5078
  if (!object3(doc.overrides)) add("overrides must be an object");
1834
5079
  else for (const [key, rule] of Object.entries(doc.overrides)) {
1835
- if (!tokenName.test(key) || ["__proto__", "constructor", "prototype"].includes(key)) add(`Invalid override token ${key}`);
5080
+ if (!tokenName.test(key) || ["__proto__", "constructor", "prototype"].includes(key)) add(`Invalid override token ${key}. ${tokenNameHelp}`);
1836
5081
  if (!object3(rule) || !tokenValue(rule.value) || !nonempty2(rule.reason) || rule.overrideRequired !== void 0 && typeof rule.overrideRequired !== "boolean") add(`Override ${key} requires value and a reason; overrideRequired must be boolean`);
1837
5082
  }
1838
5083
  }
@@ -2094,7 +5339,7 @@ function canonicalFiles(files) {
2094
5339
 
2095
5340
  // packages/cli/src/io.ts
2096
5341
  import { constants } from "node:fs";
2097
- import { lstat, mkdir, open, readdir, realpath, rename, unlink } from "node:fs/promises";
5342
+ import { lstat, mkdir, open, readdir, realpath, rename, unlink, link } from "node:fs/promises";
2098
5343
  import { dirname, join, relative, resolve, sep } from "node:path";
2099
5344
  import { randomUUID } from "node:crypto";
2100
5345
  async function ensureRoot(directory, create = false) {
@@ -2137,22 +5382,16 @@ async function atomicWrite(root, path, content, options = {}) {
2137
5382
  const target = await checkedPath(root, path, options.document ?? true);
2138
5383
  await mkdir(dirname(target), { recursive: true });
2139
5384
  await checkedPath(root, path, options.document ?? true);
2140
- if (options.overwrite === false) {
2141
- const handle2 = await open(target, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, options.mode ?? 420);
2142
- try {
2143
- await handle2.writeFile(content, "utf8");
2144
- } finally {
2145
- await handle2.close();
2146
- }
2147
- return;
2148
- }
2149
5385
  const temporary = join(dirname(target), `.eh-${randomUUID()}.tmp`);
2150
5386
  const handle = await open(temporary, "wx", options.mode ?? 420);
2151
5387
  try {
2152
5388
  await handle.writeFile(content, "utf8");
2153
5389
  await handle.close();
2154
5390
  await checkedPath(root, path, options.document ?? true);
2155
- await rename(temporary, target);
5391
+ if (options.overwrite === false) {
5392
+ await link(temporary, target);
5393
+ await unlink(temporary);
5394
+ } else await rename(temporary, target);
2156
5395
  } catch (error) {
2157
5396
  await handle.close().catch(() => {
2158
5397
  });
@@ -2190,6 +5429,28 @@ async function readWorkspace(root) {
2190
5429
  await walk("videos");
2191
5430
  return canonicalFiles(files);
2192
5431
  }
5432
+ async function readProjectWorkspace(root, projectId) {
5433
+ assertIdentifier(projectId);
5434
+ const projectPath = `videos/${projectId}/project.json`;
5435
+ const projectContent = await readText(root, projectPath);
5436
+ const checked = validateDocument(projectPath, projectContent);
5437
+ if (!checked.valid) throw new ValidationError(checked.issues);
5438
+ const project = JSON.parse(projectContent);
5439
+ const files = { [projectPath]: projectContent };
5440
+ for (const path of /* @__PURE__ */ new Set([...projectSources(project), ...optionalProjectSources(project)])) {
5441
+ if (path === projectPath) continue;
5442
+ const aliases2 = path.startsWith(".editorbrain/") ? [path, path.replace(/^\.editorbrain\//, ".editorharness/")] : [path];
5443
+ for (const alias of aliases2) {
5444
+ try {
5445
+ files[alias] = await readText(root, alias);
5446
+ } catch (error) {
5447
+ if (error.code !== "ENOENT") throw error;
5448
+ }
5449
+ }
5450
+ }
5451
+ if (await readText(root, projectPath) !== projectContent) throw new Error("The selected project changed while reading its context. Retry before editing.");
5452
+ return canonicalFiles(files);
5453
+ }
2193
5454
 
2194
5455
  // packages/cli/src/client.ts
2195
5456
  import { setTimeout as delay } from "node:timers/promises";
@@ -2938,7 +6199,7 @@ async function setup(options, runner = runHost) {
2938
6199
  }
2939
6200
 
2940
6201
  // packages/cli/src/index.ts
2941
- import { chmod, lstat as lstat8, mkdir as mkdir6, open as open9 } from "node:fs/promises";
6202
+ import { chmod, lstat as lstat9, mkdir as mkdir6, open as open9 } from "node:fs/promises";
2942
6203
  import { constants as constants9 } from "node:fs";
2943
6204
  import { homedir } from "node:os";
2944
6205
  import { basename as basename2, dirname as dirname7, extname as extname4, join as join3, resolve as resolve7 } from "node:path";
@@ -3899,7 +7160,7 @@ async function searchProvider(provider, query, limit, client, report) {
3899
7160
  item.author = firstText(metadata.photographer, metadata.secondary_creator);
3900
7161
  item.credit = firstText(metadata.center);
3901
7162
  item.dates.created = firstText(metadata.date_created);
3902
- item.previewUrl = array2(entry.links).map(record9).filter((link2) => link2.rel === "preview").map((link2) => safeUrl(link2.href, HOSTS[provider])).find(Boolean) ?? null;
7163
+ item.previewUrl = array2(entry.links).map(record9).filter((link3) => link3.rel === "preview").map((link3) => safeUrl(link3.href, HOSTS[provider])).find(Boolean) ?? null;
3903
7164
  }
3904
7165
  add(item);
3905
7166
  }
@@ -4021,7 +7282,7 @@ async function searchMediaSources(options, dependencies = {}) {
4021
7282
 
4022
7283
  // packages/cli/src/media-inventory.ts
4023
7284
  import { createHash as createHash4 } from "node:crypto";
4024
- import { link, lstat as lstat6, mkdir as mkdir3, mkdtemp, opendir, readFile as readFile2, rm } from "node:fs/promises";
7285
+ import { link as link2, lstat as lstat6, mkdir as mkdir3, mkdtemp, opendir, readFile as readFile2, rm } from "node:fs/promises";
4025
7286
  import { basename, dirname as dirname4, extname as extname2, join as join2 } from "node:path";
4026
7287
  var GENERATED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "out", "output", "outputs", "renders", "render", "cache", "caches", "proxies", "proxy", "samples", "thumbnails", "coverage", "venv", "__pycache__"]);
4027
7288
  function mediaFailure(error) {
@@ -4130,7 +7391,7 @@ async function sampleMedia(options) {
4130
7391
  const bytes = await readFile2(temporary);
4131
7392
  if (!bytes.length) throw new MediaToolError("MEDIA_SAMPLE_EMPTY", "The requested frame did not produce a usable image.");
4132
7393
  try {
4133
- await link(temporary, output2);
7394
+ await link2(temporary, output2);
4134
7395
  } catch (error) {
4135
7396
  if (error.code === "EEXIST") throw new MediaToolError("MEDIA_OUTPUT_EXISTS", "A sample output already exists. Choose a new output directory; existing files are never overwritten.");
4136
7397
  throw error;
@@ -6537,6 +9798,43 @@ Preparation is not a rendered video or a quality certification. Inspect text fit
6537
9798
  }, { migrateLegacy: false });
6538
9799
  }
6539
9800
 
9801
+ // packages/cli/src/authored-motion.ts
9802
+ import { lstat as lstat8 } from "node:fs/promises";
9803
+ async function downloadAuthoredSource(client, pin, root, out, fetcher = fetch) {
9804
+ if (!/^[a-z0-9][a-z0-9-]{0,79}$/.test(pin.id) || !/^\d+\.\d+\.\d+$/.test(pin.version) || !/^[a-f0-9]{64}$/.test(pin.sha256) || !Number.isSafeInteger(pin.bytes) || pin.bytes < 1 || pin.bytes > 16 * 1024 * 1024) throw new CliError("INVALID_ARGUMENT", "Invalid pinned scene bundle.", "Retrieve one authored scene from the catalog.");
9805
+ const target = await checkedPath(root, out, false);
9806
+ try {
9807
+ await lstat8(target);
9808
+ throw new CliError("FILE_EXISTS", "The output already exists.", "Choose a new archive path.");
9809
+ } catch (error) {
9810
+ if (error.code !== "ENOENT") throw error;
9811
+ }
9812
+ const metadata = await client.request(`/v1/authored-motion/${pin.id}/source?${new URLSearchParams({ version: pin.version, sha256: pin.sha256 })}`);
9813
+ const url2 = new URL(metadata.url);
9814
+ const expectedPath = `/v1/authored-motion-download/${pin.id}/${pin.version}/${pin.sha256}`;
9815
+ if (metadata.sha256 !== pin.sha256 || metadata.bytes !== pin.bytes || url2.origin !== new URL(client.url).origin || url2.pathname !== expectedPath || url2.username || url2.password || url2.hash) throw new CliError("INVALID_RESPONSE", "Scene bundle metadata differs from the selected version.", "Do not use this source. Retrieve the pinned catalog entry again.");
9816
+ const response = await fetcher(url2, { redirect: "error", signal: AbortSignal.timeout(9e4) });
9817
+ if (!response.ok) throw new CliError("DOWNLOAD_FAILED", `Scene download failed (HTTP ${response.status}).`, "Request a new source link; existing archives were preserved.");
9818
+ const reader = response.body?.getReader();
9819
+ if (!reader) throw new CliError("INVALID_RESPONSE", "Scene download body is missing.", "Retry the pinned source.");
9820
+ const chunks = [];
9821
+ let size = 0;
9822
+ for (; ; ) {
9823
+ const { done, value } = await reader.read();
9824
+ if (done) break;
9825
+ size += value.length;
9826
+ if (size > pin.bytes) {
9827
+ await reader.cancel();
9828
+ throw new CliError("HASH_MISMATCH", "Scene archive exceeds the pinned length.", "Discard the unverified download.");
9829
+ }
9830
+ chunks.push(value);
9831
+ }
9832
+ const bytes = Buffer.concat(chunks);
9833
+ if (size !== pin.bytes || await sha256(bytes) !== pin.sha256) throw new CliError("HASH_MISMATCH", "Scene archive verification failed.", "Do not extract this download.");
9834
+ await atomicWrite(root, out, bytes, { document: false, overwrite: false });
9835
+ return { id: pin.id, version: pin.version, path: target, sha256: pin.sha256, bytes: size, extracted: false, executed: false, rendered: false, next: "Inspect bundle README and selected card; extract into a new local directory. Bind current project colors, fonts, language, content, timing and sound before rendering. The demo is not your finished edit." };
9836
+ }
9837
+
6540
9838
  // packages/cli/src/index.ts
6541
9839
  var HELP = `EditorBrain \u2014 portable editorial context for AI video agents
6542
9840
 
@@ -6574,7 +9872,7 @@ editorbrain integrations list [--details]
6574
9872
  editorbrain integrations set <provider-id> --file settings.json --expected-revision N
6575
9873
  editorbrain search brief --file <editorial-brief.json> [--project id] [--mode styles|events|both] [--review direct|directions|sample] [--details]
6576
9874
  editorbrain search edit [--script file | --video file | --asset sha256 | --project id] [--requirements text] [--assets tag,tag] [--mode styles|events|both] [--review direct|directions|sample]
6577
- editorbrain library search [query]
9875
+ editorbrain library search [query] [--limit 10] [--after video-id]
6578
9876
  editorbrain library get <video-id> [--document relative-source-path] [--sha256 source-hash]
6579
9877
  editorbrain library media <video-id> --sha256 media-hash --out file
6580
9878
  editorbrain narrative search [query] [--kind hook|cta|...] [--channel visual|spoken|...] [--limit 10]
@@ -6584,6 +9882,10 @@ editorbrain styles create <style-id> [--name name] [--dir folder]
6584
9882
  editorbrain styles check <style-id> [--dir folder]
6585
9883
  editorbrain media styles [--category image|motion]
6586
9884
  editorbrain media style <style-id> [--style-version 1.0.0]
9885
+ editorbrain motion scenes [query] [--limit 6] [--after cursor]
9886
+ editorbrain motion scene <scene-id> [--scene-version 1.0.0]
9887
+ editorbrain motion preview <scene-id> --scene-version 1.0.0 --ratio wide|square|tall [--preview-kind video|poster]
9888
+ editorbrain motion source <scene-id> --scene-version 1.0.0 --out .editorbrain-local/scene.tar.gz [--dir folder]
6587
9889
  editorbrain motion list [query]
6588
9890
  editorbrain motion get <component-id> [--component-version 1.0.0]
6589
9891
  editorbrain motion prepare --file scene.json --project ID --out .editorbrain-local/new-scene [--dir folder]
@@ -6623,6 +9925,10 @@ function parseArgs(argv) {
6623
9925
  const booleans = /* @__PURE__ */ new Set(["help", "version", "offline", "dry-run", "key-stdin", "invite-stdin", "no-browser", "skip-mcp", "all", "share-authorized", "details", "include-unknown"]);
6624
9926
  const known = /* @__PURE__ */ new Set(["manifest", "query", "provider", "licenses", "input", "max-files", "at", "profile", "inventory", "plan", "dir", "brand", "style", "project", "topic", "goal", "name", "adapter", "out", "url", "document", "mime", "label", "role", "limit", "cursor", "timeout", "assets", "capabilities", "duration", "agent", "workspace", "prefix", "sha256", "kind", "channel", "epistemic-status", "event-offset", "relation-offset", "script", "video", "asset", "requirements", "mode", "review", "previous-version", "previous-context-version", "category", "style-version", "file", "revision", "expected-revision", "before", "scope", "id", "text", "conditions", "status", "index", "title", "days", ...booleans]);
6625
9927
  known.add("component-version");
9928
+ known.add("after");
9929
+ known.add("scene-version");
9930
+ known.add("ratio");
9931
+ known.add("preview-kind");
6626
9932
  for (let i = 0; i < argv.length; i++) {
6627
9933
  const arg = argv[i];
6628
9934
  if (!arg.startsWith("--")) {
@@ -6651,7 +9957,7 @@ async function configRoot(create = false) {
6651
9957
  let path = process.env.EDITORBRAIN_CONFIG_DIR ?? process.env.EDITORHARNESS_CONFIG_DIR ?? join3(homedir(), ".config", "editorbrain");
6652
9958
  if (!create && !process.env.EDITORBRAIN_CONFIG_DIR && !process.env.EDITORHARNESS_CONFIG_DIR) {
6653
9959
  try {
6654
- await lstat8(path);
9960
+ await lstat9(path);
6655
9961
  } catch (error) {
6656
9962
  if (error.code !== "ENOENT") throw error;
6657
9963
  path = join3(homedir(), ".config", "editorharness");
@@ -6702,7 +10008,7 @@ async function init(args) {
6702
10008
  for (const path of Object.keys(files)) {
6703
10009
  const target = await checkedPath(root, path);
6704
10010
  try {
6705
- await lstat8(target);
10011
+ await lstat9(target);
6706
10012
  throw new Error(`Refusing to replace existing file: ${path}`);
6707
10013
  } catch (error) {
6708
10014
  if (error.code !== "ENOENT") throw error;
@@ -6714,7 +10020,7 @@ async function init(args) {
6714
10020
  for (const path of directories) {
6715
10021
  const target = await checkedPath(root, path, false);
6716
10022
  try {
6717
- if (!(await lstat8(target)).isDirectory()) throw new Error(`Not a directory: ${path}`);
10023
+ if (!(await lstat9(target)).isDirectory()) throw new Error(`Not a directory: ${path}`);
6718
10024
  } catch (error) {
6719
10025
  if (error.code !== "ENOENT") throw error;
6720
10026
  }
@@ -6781,6 +10087,34 @@ async function main() {
6781
10087
  return;
6782
10088
  }
6783
10089
  if (args.flags.details && !["media", "production", "integrations"].includes(command) && !(command === "search" && subcommand === "brief")) throw new CliError("INVALID_ARGUMENT", "--details is supported only by media, production, integrations list and search brief.", "Read editorbrain help.");
10090
+ if (command === "motion" && ["scenes", "scene", "source", "preview"].includes(subcommand)) {
10091
+ const allowed = new Set(subcommand === "scenes" ? ["limit", "after"] : subcommand === "scene" ? ["scene-version"] : subcommand === "preview" ? ["scene-version", "ratio", "preview-kind", "url"] : ["scene-version", "out", "dir", "url"]);
10092
+ for (const key of Object.keys(args.flags)) if (!allowed.has(key)) throw new CliError("INVALID_ARGUMENT", `--${key} is not supported here.`, "Read editorbrain help.");
10093
+ if (subcommand === "scenes") {
10094
+ const raw = str(args, "limit");
10095
+ if (raw !== void 0 && !/^\d+$/.test(raw)) throw new Error("--limit must be an integer");
10096
+ output(listAuthoredMotionScenes({ query: args.positional.slice(2).join(" "), limit: raw === void 0 ? void 0 : Number(raw), after: str(args, "after") }));
10097
+ } else {
10098
+ if (!argument || args.positional.length !== 3) throw new Error("Provide one authored scene ID");
10099
+ const version = str(args, "scene-version");
10100
+ const scene = getAuthoredMotionCard(argument, version);
10101
+ if (!scene) throw new CliError("NOT_FOUND", "Authored scene or exact version is not bundled with this CLI.", "Search motion scenes. No other version was substituted.");
10102
+ if (subcommand === "scene") {
10103
+ const { objectKey, ...bundle } = scene.bundle;
10104
+ output({ ...scene, bundle });
10105
+ } else if (subcommand === "preview") {
10106
+ const ratio = str(args, "ratio"), kind = str(args, "preview-kind") ?? "video";
10107
+ if (!version || !ratio || !["wide", "square", "tall"].includes(ratio) || !["video", "poster"].includes(kind)) throw new Error("motion preview requires --scene-version, --ratio wide|square|tall and optional --preview-kind video|poster");
10108
+ output(await new Client(await config(args)).request(`/v1/authored-motion/${scene.id}/preview?${new URLSearchParams({ version, ratio, kind })}`));
10109
+ } else {
10110
+ const out = str(args, "out");
10111
+ if (!out || !version) throw new Error("motion source requires --scene-version and --out");
10112
+ const root2 = await ensureRoot(str(args, "dir") ?? process.cwd());
10113
+ output(await downloadAuthoredSource(new Client(await config(args)), { id: scene.id, version: scene.version, sha256: scene.bundle.sha256, bytes: scene.bundle.bytes }, root2, out));
10114
+ }
10115
+ }
10116
+ return;
10117
+ }
6784
10118
  if (command === "motion") {
6785
10119
  const allowed = new Set(subcommand === "list" ? [] : subcommand === "get" ? ["component-version"] : subcommand === "prepare" ? ["dir", "file", "project", "out"] : []);
6786
10120
  if (!["list", "get", "prepare"].includes(subcommand)) throw new CliError("INVALID_ARGUMENT", "Use motion list, get or prepare.", "Read editorbrain help.");
@@ -6977,7 +10311,7 @@ async function main() {
6977
10311
  const outRoot = await ensureRoot(resolve7(str(args, "dir") ?? process.cwd(), destination), true);
6978
10312
  for (const path of Object.keys(result2.files)) {
6979
10313
  try {
6980
- await lstat8(await checkedPath(outRoot, path, false));
10314
+ await lstat9(await checkedPath(outRoot, path, false));
6981
10315
  throw new Error(`Refusing to replace existing proposal: ${path}`);
6982
10316
  } catch (error) {
6983
10317
  if (error.code !== "ENOENT") throw error;
@@ -7203,7 +10537,18 @@ async function main() {
7203
10537
  if (command === "library") {
7204
10538
  const client = new Client(await config(args));
7205
10539
  if (subcommand === "search") {
7206
- output(await client.request(`/v1/library?query=${encodeURIComponent(args.positional.slice(2).join(" "))}`));
10540
+ for (const key of Object.keys(args.flags)) if (!["url", "limit", "after"].includes(key)) throw new CliError("INVALID_ARGUMENT", `--${key} is not supported for library search.`, "Read editorbrain help.");
10541
+ const params = new URLSearchParams({ query: args.positional.slice(2).join(" ") });
10542
+ const limit = str(args, "limit"), after = str(args, "after");
10543
+ if (limit !== void 0) {
10544
+ if (!/^\d+$/.test(limit) || Number(limit) < 1 || Number(limit) > 20) throw new Error("--limit must be 1 through 20");
10545
+ params.set("limit", limit);
10546
+ }
10547
+ if (after !== void 0) {
10548
+ if (!after.trim() || after.length > 256 || /[\x00-\x1f\x7f]/.test(after)) throw new Error("Invalid --after cursor");
10549
+ params.set("after", after);
10550
+ }
10551
+ output(await client.request(`/v1/library?${params}`));
7207
10552
  return;
7208
10553
  }
7209
10554
  if (subcommand === "get" && argument) {
@@ -7247,14 +10592,18 @@ async function main() {
7247
10592
  if (!["validate", "resolve", "export", "push", "pull"].includes(command)) throw new Error(`Unknown command: ${command}`);
7248
10593
  const root = await ensureRoot(str(args, "dir") ?? process.cwd(), command === "pull" && !args.flags["dry-run"]);
7249
10594
  const run = async () => {
7250
- const files = await readWorkspace(root);
10595
+ const selectedProject = command === "resolve" || command === "export" ? subcommand ?? str(args, "project") : void 0;
10596
+ if ((command === "resolve" || command === "export") && !selectedProject) throw new Error("A project id is required");
10597
+ const files = selectedProject ? await readProjectWorkspace(root, selectedProject) : await readWorkspace(root);
7251
10598
  if (command === "pull") {
7252
10599
  output(await (args.flags.all ? pullWorkspace(new Client(await config(args)), root, files, { dryRun: !!args.flags["dry-run"] }) : pullProject(new Client(await config(args)), root, files, { projectId: str(args, "project"), dryRun: !!args.flags["dry-run"] })));
7253
10600
  return;
7254
10601
  }
7255
10602
  if (Object.keys(files).length === 0) throw new Error("No portable documents found. Run init or pull first.");
7256
- const validation = validateWorkspace(files);
7257
- if (!validation.valid) throw new ValidationError(validation.issues);
10603
+ if (command === "validate" || command === "push") {
10604
+ const validation = validateWorkspace(files);
10605
+ if (!validation.valid) throw new ValidationError(validation.issues);
10606
+ }
7258
10607
  if (command === "validate") {
7259
10608
  output({ valid: true, documents: Object.keys(files).length });
7260
10609
  return;