wizz-method 2.17.2 → 2.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/THIRD-PARTY-NOTICES.md +12 -0
- package/package.json +1 -1
- package/skills-registry.yaml +24 -6
- package/src/core-skills/_shared/handoff-protocol.md +3 -1
- package/src/modules/wizz/_shared/despacho-barato.md +1 -1
- package/src/modules/wizz/_shared/model-ladder.md +10 -0
- package/src/modules/wizz/_shared/video-pipeline.md +10 -0
- package/src/modules/wizz/agents/wizz-maestro/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-social/SKILL.md +2 -0
- package/src/modules/wizz/agents/wizz-social/customize.toml +10 -0
- package/src/modules/wizz/subagents/codex/wizz-exec-standard.toml +1 -1
- package/src/modules/wizz/subagents/codex/wizz-exec-strong.toml +1 -1
- package/src/modules/wizz/subagents/gemini/wizz-exec-standard.md +1 -1
- package/src/modules/wizz/subagents/gemini/wizz-exec-strong.md +1 -1
- package/src/modules/wizz/subagents/opencode/wizz-exec-standard.md +1 -1
- package/src/modules/wizz/subagents/opencode/wizz-exec-strong.md +1 -1
- package/src/modules/wizz/subagents/spec.yaml +6 -6
- package/src/modules/wizz/subagents/wizz-exec-standard.md +1 -1
- package/src/modules/wizz/subagents/wizz-exec-strong.md +1 -1
- package/src/skills-lib/wizz-hook-extractor/LICENSE +21 -0
- package/src/skills-lib/wizz-hook-extractor/SKILL.md +56 -0
- package/src/skills-lib/wizz-hook-extractor/references/examples.md +59 -0
- package/src/skills-lib/wizz-hook-extractor/references/formulas.md +136 -0
- package/src/skills-lib/wizz-hook-extractor/scripts/read-post.sh +83 -0
- package/src/skills-lib/wizz-ig-profile-optimizer/LICENSE +21 -0
- package/src/skills-lib/wizz-ig-profile-optimizer/SKILL.md +74 -0
- package/src/skills-lib/wizz-ig-profile-optimizer/scripts/capture-profile.sh +41 -0
- package/src/skills-lib/wizz-ig-profile-optimizer/scripts/limits.mjs +82 -0
- package/src/skills-lib/wizz-router/references/routing-table-flat.md +3 -1
- package/src/skills-lib/wizz-video-effects/SKILL.md +1 -0
- package/src/skills-lib/wizz-video-styles/SKILL.md +9 -3
- package/src/skills-lib/wizz-video-styles/data/recipes.json +33 -1
- package/src/skills-lib/wizz-video-styles/data/references.json +175 -0
- package/src/skills-lib/wizz-video-styles/data/styles.json +55 -0
- package/src/skills-lib/wizz-video-styles/references/generated-prompts.md +60 -0
- package/src/skills-lib/wizz-video-styles/references/measured-patterns.md +2 -0
- package/src/skills-lib/wizz-video-styles/references/style-picker.md +5 -3
- package/src/skills-lib/wizz-video-styles/references/styles/tipografia-cinetica.md +68 -0
- package/src/skills-lib/wizz-video-styles/scripts/analyze.sh +2 -1
- package/src/skills-lib/wizz-video-styles/scripts/beat_grid.py +68 -0
- package/src/skills-lib/wizz-video-styles/scripts/references.mjs +264 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Hook formulas and post shapes, mapped to the Blueprint types
|
|
2
|
+
|
|
3
|
+
The Method already numbers hooks: the **7 types** of the Viral Script Blueprint (`wizz-social` agent, `knowledge/blueprint-roteiro-viral.md`, section 1). This skill classifies the hook with **those** types, so an extracted mould drops straight into a script. What the Blueprint does not cover is the **shape of the post** around the hook (a caption cut at the "mais" fold, a carousel slide by slide, a Reel's first seconds). The 10 shapes below cover that; they are adapted from sergebulaev/instagram-skills (MIT, credited in `THIRD-PARTY-NOTICES.md`).
|
|
4
|
+
|
|
5
|
+
## 1. Hook → Blueprint type
|
|
6
|
+
|
|
7
|
+
| Type | Name | How to spot it in the first line / slide 1 / first 3 s |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| Tipo 01 | A Façanha Pessoal | past tense + an absurd or precise result of the author ("eu fiz X em 47 minutos") |
|
|
10
|
+
| Tipo 02 | A Sentença de Morte | a tool, job or company declared dead ("X acabou de matar Y") |
|
|
11
|
+
| Tipo 03 | O Comando Provocador | an order that sounds radical ("pode demitir...", "para de postar...") |
|
|
12
|
+
| Tipo 04 | A Pergunta de Resultado | a question about a specific result the viewer wants |
|
|
13
|
+
| Tipo 05 | A Lista Urgente | a number + things/errors/prompts ("5 erros que...") |
|
|
14
|
+
| Tipo 06 | A Revelação de Erro | points at something the viewer does wrong, with a cost ("se você faz X, está perdendo Y") |
|
|
15
|
+
| Tipo 07 | O Breaking News | a fresh piece of news as a headline, with "a partir de hoje" |
|
|
16
|
+
| fora dos 7 | identificação / confissão | see the equivalence table: two Instagram shapes have no Blueprint type |
|
|
17
|
+
|
|
18
|
+
## 2. Shape → Blueprint type (equivalence)
|
|
19
|
+
|
|
20
|
+
The 10 shapes do not map 1 to 1. This table is the single place where they meet; never number hooks with IG1..IG10 in a delivery, use the Tipo and the shape name.
|
|
21
|
+
|
|
22
|
+
| Shape (surface) | Closest Blueprint type | Note |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| Resultado com número (legenda) | Tipo 01 | Tipo 01 with an odd, measured number |
|
|
25
|
+
| Verdade contrária (legenda) | Tipo 03, or Tipo 02 when something is declared dead | |
|
|
26
|
+
| Identificação (legenda) | **fora dos 7** (closest: Tipo 06 without the guilt) | a shared moment, no number, no lesson |
|
|
27
|
+
| Confissão curta (legenda ou Reel) | **fora dos 7** (closest: Tipo 01 told from the failure) | a real cost, then the lesson |
|
|
28
|
+
| Lista (carrossel) | Tipo 05 | |
|
|
29
|
+
| Antes e depois (carrossel) | Tipo 01 | Blueprint phase 04 "resultado visual" moved to slide 1 |
|
|
30
|
+
| Mito e verdade (carrossel) | Tipo 06 | |
|
|
31
|
+
| Framework (carrossel) | Tipo 01 + the Blueprint's "Revelação de Sistema" structure | |
|
|
32
|
+
| Quebra de padrão (Reel) | Tipo 02, 03 or 07: the shape is how it is delivered, the type is what is said | |
|
|
33
|
+
| "Como eu fiz" (Reel) | Tipo 01 + the Blueprint's "Tutorial Bombástico" structure | |
|
|
34
|
+
|
|
35
|
+
Tipo 04 and Tipo 07 have no Instagram shape of their own: they live in any surface.
|
|
36
|
+
|
|
37
|
+
## 3. The 10 shapes
|
|
38
|
+
|
|
39
|
+
Moulds use `{slots}` and Brazilian Portuguese with "você". Goal = what the shape tends to earn.
|
|
40
|
+
|
|
41
|
+
### Legenda (single image or any post): the first ~125 characters, before "mais"
|
|
42
|
+
|
|
43
|
+
**Resultado com número** · saves
|
|
44
|
+
```
|
|
45
|
+
{resultado com número exato} em {prazo}.
|
|
46
|
+
|
|
47
|
+
O que eu mudei está aqui embaixo.
|
|
48
|
+
```
|
|
49
|
+
Odd numbers (47 minutos, R$ 873) read as measured. Never invent the number.
|
|
50
|
+
|
|
51
|
+
**Verdade contrária** · shares
|
|
52
|
+
```
|
|
53
|
+
{conselho que todo mundo repete} está errado.
|
|
54
|
+
|
|
55
|
+
{o que funciona, dito sem rodeio}.
|
|
56
|
+
```
|
|
57
|
+
Only claims the author would defend in the comments.
|
|
58
|
+
|
|
59
|
+
**Identificação** · comments
|
|
60
|
+
```
|
|
61
|
+
{um momento específico que todo mundo do nicho conhece, sem contexto}
|
|
62
|
+
```
|
|
63
|
+
Specific beats generic ("abrir o Canva pra fazer uma arte rápida e fechar 90 minutos depois").
|
|
64
|
+
|
|
65
|
+
**Confissão curta** · comments and follows
|
|
66
|
+
```
|
|
67
|
+
{marcador de tempo}: {o que aconteceu, incluindo a parte feia}.
|
|
68
|
+
|
|
69
|
+
{a linha de sentido que faz valer a leitura}.
|
|
70
|
+
```
|
|
71
|
+
It has to be true.
|
|
72
|
+
|
|
73
|
+
### Carrossel: slide 1 promises, the last slide pays
|
|
74
|
+
|
|
75
|
+
**Lista** · saves
|
|
76
|
+
```
|
|
77
|
+
Slide 1: {N} {coisas} que {resultado grande e específico}. (quase todo mundo erra a {k})
|
|
78
|
+
Slides 2..N-1: um item por slide, com exemplo concreto.
|
|
79
|
+
Último: resumo em uma linha + "salva pra usar depois".
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Antes e depois** · saves and follows
|
|
83
|
+
```
|
|
84
|
+
Slide 1: {o depois, com número ou imagem forte}.
|
|
85
|
+
Slide 2: de onde começou (o antes).
|
|
86
|
+
Slides 3..N-1: os passos, um por slide.
|
|
87
|
+
Último: "segue pra ver o sistema completo de {tema}" + pedido de salvar.
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Mito e verdade** · shares
|
|
91
|
+
```
|
|
92
|
+
Slide 1: {N} mitos de {tema} que estão te custando {perda específica}.
|
|
93
|
+
Slides 2..N-1: "Mito: {crença}." / "Verdade: {o que funciona}."
|
|
94
|
+
Último: a virada em uma linha + "manda pra quem ainda acredita no 1".
|
|
95
|
+
```
|
|
96
|
+
Never invent the myth to knock it down.
|
|
97
|
+
|
|
98
|
+
**Framework** · saves
|
|
99
|
+
```
|
|
100
|
+
Slide 1: o {nome} que eu uso pra {resultado}.
|
|
101
|
+
Slides 2..N-1: uma parte por slide, com o porquê.
|
|
102
|
+
Último: o framework inteiro num slide só + pedido de salvar.
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Reel: the first 1 to 3 seconds (image + text on screen + first spoken line)
|
|
106
|
+
|
|
107
|
+
**Quebra de padrão** · shares
|
|
108
|
+
```
|
|
109
|
+
Texto do 1º quadro: {afirmação ou pergunta que faz passar reto parecer perda}.
|
|
110
|
+
1ª fala: {direto ao ponto, sem "e aí, pessoal"}.
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**"Como eu fiz"** · saves
|
|
114
|
+
```
|
|
115
|
+
Texto do 1º quadro: como eu {resultado com número} em {prazo}.
|
|
116
|
+
Corpo: passo 1, 2, 3 com rótulo na tela, cortes rápidos, sem enchimento.
|
|
117
|
+
Fim: "salva pra fazer junto" + resumo de uma linha na tela.
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## 4. Goal of the original
|
|
121
|
+
|
|
122
|
+
| Close / structure | Goal |
|
|
123
|
+
|---|---|
|
|
124
|
+
| "salva", list, framework, how-to | saves |
|
|
125
|
+
| "manda pra...", contrary take, myth | shares |
|
|
126
|
+
| identification, question, confession | comments |
|
|
127
|
+
| proof of transformation, "segue pra..." | follows |
|
|
128
|
+
|
|
129
|
+
## 5. What not to copy from a viral post
|
|
130
|
+
|
|
131
|
+
- Dashes gluing clauses together, and in text a third party reads, no em dash at all (Wizz rule).
|
|
132
|
+
- An AI-vocabulary cluster ("alavancar", "fundamentalmente", "eleve", "potencialize") in one paragraph.
|
|
133
|
+
- 20 to 30 hashtags at the top of the caption.
|
|
134
|
+
- Engagement bait ("comenta SIM", "marca 3 amigos"): Instagram pushes it down.
|
|
135
|
+
- A hook that only makes sense after the "mais" fold.
|
|
136
|
+
- Images and videos mixed in one carousel.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Reads one public Instagram post so its hook can be studied without the user pasting anything.
|
|
3
|
+
# Usage: read-post.sh <instagram_url> <out_dir>
|
|
4
|
+
# → <out_dir>/post.json (caption, author, metrics, surface), head.png (first 3 s, Reels),
|
|
5
|
+
# sheet.png (whole Reel), transcript.json (first 10 s of speech, local Whisper).
|
|
6
|
+
# Exit 0 = read; 3 = Instagram asked for a login (see the message); 2 = usage or missing tool.
|
|
7
|
+
# Login is never automatic: set WIZZ_IG_COOKIES_BROWSER=<browser> (chrome, opera, safari...) only
|
|
8
|
+
# after the user agreed to use their own logged-in browser session for this read.
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
[ $# -eq 2 ] || { echo "uso: read-post.sh <url do instagram> <pasta de saída>" >&2; exit 2; }
|
|
11
|
+
url="$1"; out="$2"
|
|
12
|
+
[[ "$url" =~ ^https://(www\.)?instagram\.com/([A-Za-z0-9._]+/)?(reel|reels|p|tv)/[A-Za-z0-9_-]+/?(\?.*)?$ ]] \
|
|
13
|
+
|| { echo "url inválida (esperado instagram.com/reel/..., /p/... ou /tv/...): $url" >&2; exit 2; }
|
|
14
|
+
for bin in yt-dlp ffmpeg node; do command -v "$bin" >/dev/null || { echo "falta $bin no PATH" >&2; exit 2; }; done
|
|
15
|
+
mkdir -p "$out"; cd "$out"
|
|
16
|
+
trap 'rm -f ./media.* ./head.wav ./raw.json.part' EXIT
|
|
17
|
+
|
|
18
|
+
auth=()
|
|
19
|
+
[ -n "${WIZZ_IG_COOKIES_BROWSER:-}" ] && auth=(--cookies-from-browser "$WIZZ_IG_COOKIES_BROWSER")
|
|
20
|
+
if ! yt-dlp -q --no-warnings --socket-timeout 20 ${auth[@]+"${auth[@]}"} -J "$url" > raw.json 2> err.txt; then
|
|
21
|
+
if grep -qiE "login|cookies|empty media|not available" err.txt; then
|
|
22
|
+
echo "O Instagram pediu login para este post. Opções: (1) com o OK do usuário, rodar de novo com WIZZ_IG_COOKIES_BROWSER=<navegador logado>; (2) print do post; (3) colar a legenda e o texto da tela." >&2
|
|
23
|
+
exit 3
|
|
24
|
+
fi
|
|
25
|
+
cat err.txt >&2; exit 1
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
# Caption, author and metrics; a carousel comes as a playlist of entries.
|
|
29
|
+
node - <<'JS'
|
|
30
|
+
const fs = require('node:fs');
|
|
31
|
+
const raw = JSON.parse(fs.readFileSync('raw.json', 'utf8'));
|
|
32
|
+
const entries = raw._type === 'playlist' ? raw.entries ?? [] : [raw];
|
|
33
|
+
const videos = entries.filter((e) => e && (e.vcodec && e.vcodec !== 'none' || e.duration));
|
|
34
|
+
const surface = raw._type === 'playlist' ? 'carrossel' : videos.length ? 'reel' : 'imagem';
|
|
35
|
+
const pick = (k) => raw[k] ?? entries[0]?.[k] ?? null;
|
|
36
|
+
fs.writeFileSync('post.json', JSON.stringify({
|
|
37
|
+
url: raw.webpage_url ?? raw.original_url,
|
|
38
|
+
surface,
|
|
39
|
+
slides: raw._type === 'playlist' ? entries.length : 1,
|
|
40
|
+
author: pick('uploader') ?? pick('channel'),
|
|
41
|
+
handle: pick('channel') ?? pick('uploader_id'),
|
|
42
|
+
date: pick('upload_date'),
|
|
43
|
+
caption: pick('description') ?? '',
|
|
44
|
+
likes: pick('like_count'),
|
|
45
|
+
comments: pick('comment_count'),
|
|
46
|
+
views: pick('view_count'),
|
|
47
|
+
duration_s: pick('duration'),
|
|
48
|
+
}, null, 1));
|
|
49
|
+
JS
|
|
50
|
+
rm -f raw.json err.txt
|
|
51
|
+
|
|
52
|
+
surface=$(node -p "require('./post.json').surface")
|
|
53
|
+
if [ "$surface" = "reel" ]; then
|
|
54
|
+
yt-dlp -q --no-warnings --socket-timeout 20 ${auth[@]+"${auth[@]}"} -o "media.%(ext)s" "$url"
|
|
55
|
+
media=$(ls media.* | head -1)
|
|
56
|
+
ffmpeg -loglevel error -y -t 3 -i "$media" -vf "fps=2,scale=270:-1,tile=6x1" -frames:v 1 head.png
|
|
57
|
+
d=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$media")
|
|
58
|
+
step=$(node -p "Math.max(1, $d / 8)")
|
|
59
|
+
ffmpeg -loglevel error -y -i "$media" -vf "fps=1/$step,scale=270:-1,tile=8x1" -frames:v 1 sheet.png
|
|
60
|
+
if ffmpeg -loglevel error -y -t 10 -i "$media" -vn -ac 1 -ar 16000 head.wav 2>/dev/null; then
|
|
61
|
+
# Local Whisper through the HyperFrames CLI: no API key. Multilingual model, Portuguese by default.
|
|
62
|
+
npx -y hyperframes transcribe head.wav -d . -m small -l "${WIZZ_IG_LANG:-pt}" --json >/dev/null 2>transcribe.log \
|
|
63
|
+
|| echo "transcrição falhou (ver transcribe.log); siga pelos quadros e pela legenda" >&2
|
|
64
|
+
# Whisper invents words over music with no speech ("Thank you for watching", "Legendas pela
|
|
65
|
+
# comunidade"): stretched words or too few words per second mean there was no real speech.
|
|
66
|
+
[ -f transcript.json ] && node - <<'JS'
|
|
67
|
+
const fs = require('node:fs');
|
|
68
|
+
const raw = JSON.parse(fs.readFileSync('transcript.json', 'utf8'));
|
|
69
|
+
const words = Array.isArray(raw) ? raw : raw.words ?? [];
|
|
70
|
+
const span = words.length ? words.at(-1).end - words[0].start : 0;
|
|
71
|
+
const stretched = words.some((w) => w.end - w.start > 1.2);
|
|
72
|
+
const slow = span > 0 && words.length / span < 0.8;
|
|
73
|
+
const canned = /thank you for watching|obrigad[oa] por assistir|legendas? pela comunidade|inscreva-se/i.test(words.map((w) => w.text).join(' '));
|
|
74
|
+
const post = JSON.parse(fs.readFileSync('post.json', 'utf8'));
|
|
75
|
+
post.speech = words.length === 0 ? 'sem fala' : stretched || slow || canned ? 'suspeita' : 'ok';
|
|
76
|
+
fs.writeFileSync('post.json', JSON.stringify(post, null, 1));
|
|
77
|
+
if (post.speech !== 'ok') console.error(`transcrição ${post.speech}: provavelmente música sem fala; o gancho está nos quadros e no texto da tela`);
|
|
78
|
+
JS
|
|
79
|
+
else
|
|
80
|
+
echo "Reel sem áudio: o gancho está só no texto da tela (head.png)" >&2
|
|
81
|
+
fi
|
|
82
|
+
fi
|
|
83
|
+
echo "ok: $(pwd)/post.json ($surface)"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sergey Bulaev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wizz-ig-profile-optimizer
|
|
3
|
+
description: Audits and rewrites an Instagram profile so a visitor decides to follow. Captures the public profile by the @ (photo, NAME field, @, bio, link, first grid rows), scores the nine parts (photo, NAME, @, bio, link, category, highlights, first nine of the grid, pinned posts) against the profile's goal (grow, sell, land clients, authority), and delivers a scorecard, fixes ranked by impact, before and after rewrites inside Instagram's limits (bio 150, NAME 30), a highlights plan and the pins chosen by real metrics when Buffer is connected. Use when someone asks to review, fix or optimise an Instagram profile or bio. Triggers - arruma minha bio, revisa meu perfil do instagram, o que colocar nos destaques, quais posts fixar, meu perfil não converte seguidor. Does not write captions or scripts (that is wizz-social-content) and does not design the highlight covers (that is wizz-canvas-design).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# wizz-ig-profile-optimizer: the header that earns the follow
|
|
7
|
+
|
|
8
|
+
The follow decision happens on the header and the first rows of the grid: photo, NAME, bio, link, then the grid at a glance. This skill reads that as a stranger would, scores it, and rewrites what fails.
|
|
9
|
+
|
|
10
|
+
All text delivered is Brazilian Portuguese with "você", and no em dash in anything public (bio, NAME, highlight names, pinned captions).
|
|
11
|
+
|
|
12
|
+
## Inputs
|
|
13
|
+
|
|
14
|
+
- The @ (preferred), or a screenshot of the header and the first grid rows.
|
|
15
|
+
- The goal of the profile: **crescer**, **vender**, **conseguir cliente** or **autoridade**. It changes the link, the bio's last line and the pins. Ask once if it is not clear.
|
|
16
|
+
|
|
17
|
+
## Steps
|
|
18
|
+
|
|
19
|
+
1. **Read the profile.** `scripts/capture-profile.sh <@> <out_dir>` (out_dir under `_wizz-output/`) saves `profile.png` and `profile.json` (NAME, bio and link as text, follower and post counts). **Open the image.** Logged out, Instagram may hide highlights, the category label and the pin marks: ask for a screenshot of those parts instead of guessing them.
|
|
20
|
+
2. **Pull what the Method already knows**, before judging:
|
|
21
|
+
- Positioning, audience and tone from the project's Cérebro (`/cerebro ver`).
|
|
22
|
+
- Highlights already made in the project (for example a `posts/highlights/` folder).
|
|
23
|
+
- The offer from `wizz-offer-forge`, when one exists: the link and one pin depend on it.
|
|
24
|
+
- Real performance: with Buffer connected, `get_aggregated_post_metrics` and the post list give the best posts by saves, shares and reach. Without it, ask which posts did best; never pick pins by guess.
|
|
25
|
+
3. **Score the nine parts** (table below): passa / ajustar / reprova, one line of reason each.
|
|
26
|
+
4. **Rewrite what fails**, in order of impact: NAME, bio, pins first.
|
|
27
|
+
- **NAME** = name + the word people type in search, up to 30 characters ("Ana Souza | Nutrição"). The NAME field is what Instagram search indexes, not the @.
|
|
28
|
+
- **Bio** up to 150 = who it helps + what it posts about + one proof or specific. Lead with what the follower gets, not the job title. One anchor emoji at most; line breaks are fine.
|
|
29
|
+
- **Link**: one, matched to the goal (offer, booking, newsletter); a link hub only when several are really needed.
|
|
30
|
+
- **Category** (business or creator account): one label that reinforces the bio.
|
|
31
|
+
- **Highlights**: 4 to 6, left to right in the order of the visitor's next question (Comece aqui, Resultados, Oferta, Dúvidas, Sobre), names short enough to show whole, covers in one style.
|
|
32
|
+
- **First nine**: judged as a set (consistent look, readable at a glance, range without chaos); say which tiles to move or replace.
|
|
33
|
+
- **Pins** (up to 3), by goal: a top performer, the offer, a "pra quem é" post.
|
|
34
|
+
5. **Check every rewrite**: `node scripts/limits.mjs --bio "..." --name "..." [--handle "..."] [--highlight "..."]`. It fails on bio over 150, NAME over 30, a NAME with no search word, an @ with invalid characters, a highlight name that gets cut, an em dash, or AI words. Nothing is delivered until it passes. Then run bio and NAME through `wizz-humanizer`.
|
|
35
|
+
6. **Header test.** Reading only photo + NAME + bio + link + first grid row: would a stranger know who this is for and follow? Answer yes or no, and why.
|
|
36
|
+
|
|
37
|
+
## Scorecard
|
|
38
|
+
|
|
39
|
+
| # | Part | Passes when |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| 1 | Foto | face (or mark) fills the circle, recent, high contrast, readable small |
|
|
42
|
+
| 2 | NOME | name + search word, up to 30 |
|
|
43
|
+
| 3 | @ | short, matches the brand, no numbers or underscores when avoidable |
|
|
44
|
+
| 4 | Bio | value + topic + proof in up to 150, no cliché opener, no emoji storm |
|
|
45
|
+
| 5 | Link | one, goal-matched; the top link carries the click |
|
|
46
|
+
| 6 | Categoria | set, one niche label that reinforces the bio |
|
|
47
|
+
| 7 | Destaques | 4 to 6, ordered by the visitor's next question, one cover style, names not cut |
|
|
48
|
+
| 8 | 9 primeiras | consistent look, readable, shows range; weak tiles moved or replaced |
|
|
49
|
+
| 9 | Fixados | up to 3, each the best proof for the goal, chosen by metrics |
|
|
50
|
+
|
|
51
|
+
## Delivery
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
👤 PERFIL LIDO: @ · seguidores · posts · objetivo · como foi lido (captura / print)
|
|
55
|
+
📊 PLACAR: 9 linhas, passa | ajustar | reprova + motivo
|
|
56
|
+
🔧 CORREÇÕES POR IMPACTO: lista curta
|
|
57
|
+
✍️ ANTES → DEPOIS: NOME, bio, link, categoria (só o que reprovou), com a contagem do limits.mjs
|
|
58
|
+
⭐ DESTAQUES: ordem, nomes, o que vai em cada um
|
|
59
|
+
📌 FIXADOS: 3 posts + por quê (métrica)
|
|
60
|
+
🧪 TESTE DO CABEÇALHO: sim/não + motivo
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Hand-off
|
|
64
|
+
|
|
65
|
+
- Highlight covers and the pinned posts' art: `wizz-canvas-design`.
|
|
66
|
+
- Captions of the pinned posts: `wizz-social-content`.
|
|
67
|
+
- The offer behind the link: `wizz-offer-forge`.
|
|
68
|
+
|
|
69
|
+
## Hard rules
|
|
70
|
+
|
|
71
|
+
- Never ship a bio or NAME that fails `limits.mjs`, and never a truncated highlight name.
|
|
72
|
+
- Never invent proof for the bio (a number, a client count, a result): ask, or leave a slot.
|
|
73
|
+
- Never log in to Instagram on the user's behalf; a logged-out capture plus their screenshots is enough.
|
|
74
|
+
- Highlights and grid are judged as sets: fix order and consistency, not only single items.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Captures a public Instagram profile as a logged-out visitor sees it, with agent-browser.
|
|
3
|
+
# Usage: capture-profile.sh <@handle or handle> <out_dir>
|
|
4
|
+
# → <out_dir>/profile.png (phone-width, header + first rows of the grid)
|
|
5
|
+
# <out_dir>/profile.json (page title, meta description with counts, header text: NAME, bio, link)
|
|
6
|
+
# Logged out, Instagram may hide highlights, the category label and the pin marks: ask for a
|
|
7
|
+
# screenshot of those. Exit 2 = usage or missing tool; 1 = page did not load.
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
[ $# -eq 2 ] || { echo "uso: capture-profile.sh <@perfil> <pasta de saída>" >&2; exit 2; }
|
|
10
|
+
handle="${1#@}"; out="$2"
|
|
11
|
+
[[ "$handle" =~ ^[A-Za-z0-9._]{1,30}$ ]] || { echo "@ inválido: $1" >&2; exit 2; }
|
|
12
|
+
command -v agent-browser >/dev/null || { echo "falta agent-browser no PATH (skill wizz-agent-browser)" >&2; exit 2; }
|
|
13
|
+
mkdir -p "$out"
|
|
14
|
+
trap 'agent-browser close >/dev/null 2>&1 || true' EXIT
|
|
15
|
+
|
|
16
|
+
agent-browser set viewport 430 1400 >/dev/null
|
|
17
|
+
agent-browser open "https://www.instagram.com/$handle/" >/dev/null
|
|
18
|
+
agent-browser wait 3000 >/dev/null
|
|
19
|
+
# Cookie banner (pt or en), then Escape closes the sign-up prompt that covers the grid.
|
|
20
|
+
agent-browser find text "Recusar cookies opcionais" click >/dev/null 2>&1 \
|
|
21
|
+
|| agent-browser find text "Decline optional cookies" click >/dev/null 2>&1 || true
|
|
22
|
+
agent-browser wait 1500 >/dev/null
|
|
23
|
+
agent-browser press Escape >/dev/null 2>&1 || true
|
|
24
|
+
agent-browser wait 800 >/dev/null
|
|
25
|
+
|
|
26
|
+
agent-browser eval 'JSON.stringify({
|
|
27
|
+
title: document.title,
|
|
28
|
+
description: document.querySelector("meta[property=\"og:description\"]")?.content ?? null,
|
|
29
|
+
header: document.querySelector("header")?.innerText ?? null
|
|
30
|
+
})' > "$out/profile.raw"
|
|
31
|
+
node -e '
|
|
32
|
+
const fs = require("node:fs");
|
|
33
|
+
const [raw, out] = process.argv.slice(1);
|
|
34
|
+
let data = JSON.parse(fs.readFileSync(raw, "utf8"));
|
|
35
|
+
if (typeof data === "string") data = JSON.parse(data);
|
|
36
|
+
if (!data.header) { console.error("perfil não carregou (privado, inexistente ou bloqueado)"); process.exit(1); }
|
|
37
|
+
fs.writeFileSync(out, JSON.stringify(data, null, 1));
|
|
38
|
+
' "$out/profile.raw" "$out/profile.json"
|
|
39
|
+
rm -f "$out/profile.raw"
|
|
40
|
+
agent-browser screenshot "$out/profile.png" >/dev/null
|
|
41
|
+
echo "ok: $out/profile.json + profile.png"
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Checks Instagram profile text against the platform limits and the Wizz text rules.
|
|
3
|
+
// node limits.mjs --bio "<text>" --name "<text>" [--handle "<text>"] [--highlight "<name>" ...] [--json]
|
|
4
|
+
// Exit 0 = everything fits; 1 = something fails (listed); 2 = usage.
|
|
5
|
+
// Counting is conservative: UTF-16 units, so an emoji counts as 2. Instagram's own counter may be
|
|
6
|
+
// more lenient; a text that passes here fits there.
|
|
7
|
+
|
|
8
|
+
const LIMITS = { bio: 150, name: 30, handle: 30 };
|
|
9
|
+
// Not a platform limit: above this a highlight name gets cut under the circle on most phones.
|
|
10
|
+
const HIGHLIGHT_VISIBLE = 15;
|
|
11
|
+
const AI_WORDS = /\b(alavanc\w*|fundamentalmente|potencializ\w*|eleve|elevar|revolucion\w*|game[- ]changer|leverage|elevate)\b/i;
|
|
12
|
+
const EM_DASH = /—/;
|
|
13
|
+
const HANDLE_CHARS = /^[A-Za-z0-9._]+$/;
|
|
14
|
+
|
|
15
|
+
function parse(argv) {
|
|
16
|
+
const options = { highlights: [], json: false };
|
|
17
|
+
for (let i = 0; i < argv.length; i++) {
|
|
18
|
+
const flag = argv[i];
|
|
19
|
+
if (flag === '--json') options.json = true;
|
|
20
|
+
else if (flag === '--highlight') options.highlights.push(argv[++i] ?? '');
|
|
21
|
+
else if (['--bio', '--name', '--handle'].includes(flag)) options[flag.slice(2)] = argv[++i] ?? '';
|
|
22
|
+
else usage(`unknown argument: ${flag}`);
|
|
23
|
+
}
|
|
24
|
+
if (options.bio === undefined && options.name === undefined && options.handle === undefined && options.highlights.length === 0) {
|
|
25
|
+
usage('nothing to check');
|
|
26
|
+
}
|
|
27
|
+
return options;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function usage(message) {
|
|
31
|
+
process.stderr.write(`limits: ${message}\nusage: node limits.mjs --bio "..." --name "..." [--handle "..."] [--highlight "..."] [--json]\n`);
|
|
32
|
+
process.exit(2);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const graphemes = (text) => [...new Intl.Segmenter('pt-BR', { granularity: 'grapheme' }).segment(text)].length;
|
|
36
|
+
|
|
37
|
+
function checkField(field, text, limit) {
|
|
38
|
+
const problems = [];
|
|
39
|
+
if (text.length > limit) problems.push(`${text.length} caracteres (limite ${limit}, emoji conta 2)`);
|
|
40
|
+
if (EM_DASH.test(text)) problems.push('travessão (—) em texto público');
|
|
41
|
+
const ai = text.match(AI_WORDS);
|
|
42
|
+
if (ai) problems.push(`palavra de IA: "${ai[0]}"`);
|
|
43
|
+
return { field, text, length: text.length, graphemes: graphemes(text), limit, ok: problems.length === 0, problems };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function check(options) {
|
|
47
|
+
const results = [];
|
|
48
|
+
if (options.bio !== undefined) results.push(checkField('bio', options.bio, LIMITS.bio));
|
|
49
|
+
if (options.name !== undefined) {
|
|
50
|
+
const name = checkField('nome', options.name, LIMITS.name);
|
|
51
|
+
if (!/[|·,\-]/.test(options.name) && options.name.trim().split(/\s+/).length < 3) {
|
|
52
|
+
name.problems.push('NOME sem palavra-chave de busca (ex.: "Ana Souza | Nutrição")');
|
|
53
|
+
name.ok = false;
|
|
54
|
+
}
|
|
55
|
+
results.push(name);
|
|
56
|
+
}
|
|
57
|
+
if (options.handle !== undefined) {
|
|
58
|
+
const handle = checkField('@', options.handle.replace(/^@/, ''), LIMITS.handle);
|
|
59
|
+
if (!HANDLE_CHARS.test(handle.text)) {
|
|
60
|
+
handle.problems.push('@ só aceita letras, números, ponto e sublinhado');
|
|
61
|
+
handle.ok = false;
|
|
62
|
+
}
|
|
63
|
+
results.push(handle);
|
|
64
|
+
}
|
|
65
|
+
for (const highlight of options.highlights) {
|
|
66
|
+
const result = checkField('destaque', highlight, HIGHLIGHT_VISIBLE);
|
|
67
|
+
result.problems = result.problems.map((p) => p.replace(/\(limite \d+, emoji conta 2\)/, `(acima de ${HIGHLIGHT_VISIBLE} corta embaixo do círculo)`));
|
|
68
|
+
results.push(result);
|
|
69
|
+
}
|
|
70
|
+
return results;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const options = parse(process.argv.slice(2));
|
|
74
|
+
const results = check(options);
|
|
75
|
+
if (options.json) process.stdout.write(`${JSON.stringify(results, null, 2)}\n`);
|
|
76
|
+
else {
|
|
77
|
+
for (const r of results) {
|
|
78
|
+
const mark = r.ok ? 'ok ' : 'ERRO';
|
|
79
|
+
process.stdout.write(`${mark} ${r.field}: ${r.length}/${r.limit}${r.problems.length ? ' · ' + r.problems.join(' · ') : ''}\n`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
process.exit(results.every((r) => r.ok) ? 0 : 1);
|
|
@@ -55,7 +55,7 @@ Use this table to map skills/CLIs/MCPs directly **outside a Wizz project** (no `
|
|
|
55
55
|
| **Video with audio, narration, SFX, or synced captions** (film assembly) | `wizz-remotion-best-practices` (Remotion assembles; frame timing). Pipeline rule: HyperFrames draws the mute screens, Remotion assembles the film — don't use hyperframes to position audio/captions (~2.76s drift and CSS leaking between screens in production) | 1 |
|
|
56
56
|
| Draw a video SCREEN/scene in HTML/CSS, mute visual (app mockup, animated chart, title, loop; HTML→MP4) | CLI `hyperframes` (designer registry; `npx skills add heygen-com/hyperframes`) — mute screens only; assembly with audio goes to Remotion (line above) | 2 |
|
|
57
57
|
| Word-by-word timestamps for TTS narration (sync captions/cuts/animation with speech) | `ctc-align` (designer registry; single source of timing, replaces Whisper) | 2 |
|
|
58
|
-
| Which short-video STYLE to copy, from the inventory of the material (real person, voice only, B-roll, screens, product), with the recipe measured on
|
|
58
|
+
| Which short-video STYLE to copy, from the inventory of the material (real person, voice only, B-roll, screens, product), with the recipe measured on 104 real shorts (lyric video and kinetic typography included); "edit it in the style of", "the video looks amateur" | `wizz-video-styles` (designer registry; picks the style before voice, timing, effects and assembly) | 2 |
|
|
59
59
|
| Effects and sound of a short vertical video (ad or Reels): which effect goes where and when not, which SFX for each event and at what level, ducking, master loudness, safe-zone/density/excess/rhythm gates | `wizz-video-effects` (designer registry; decides the effects, Remotion assembles them) | 2 |
|
|
60
60
|
| **Find B-roll / inserts / cutaways / supporting footage** for a video or Reel (real footage of the fact, person, product, news item or screen the script names; YouTube/Instagram/TikTok/Pexels/Pixabay/Commons/NASA) | CLI `get-brolls` (designer registry; **macOS**, global `skills add` + its own `install.sh`; needs ffmpeg with drawtext, Node 22+, Python 3.11+). Ends in a Storyboard the HUMAN approves — the agent never self-approves. Stock only when the user explicitly asks for stock | 1 |
|
|
61
61
|
| **Edit/cut video** (cuts, selects, roughcut) | CLI `buttercut` (designer registry; **Apple Silicon only**, clone-and-run in the project, heavy deps) | 1 |
|
|
@@ -84,6 +84,8 @@ Use this table to map skills/CLIs/MCPs directly **outside a Wizz project** (no `
|
|
|
84
84
|
| EXECUTE the SEO distribution: publish articles, build backlinks through the Distribb network, connect Search Console, autopublish news | CLI `distribb` (seo registry) — the doing half; the strategy half is the SEO row above | 2 |
|
|
85
85
|
| Competitor comparison, alternatives page, war room | `wizz-competitor-alternatives` | 2 |
|
|
86
86
|
| Content, social media, newsletter, threads | `wizz-content-strategy` + `wizz-social-content` + `wizz-copy-editing` | 2 |
|
|
87
|
+
| Tear down ONE viral Instagram post (Reel, carousel, caption) from its link: why it worked, which hook, a mould to do one like it | `wizz-hook-extractor` (social registry; reads the post itself) | 2 |
|
|
88
|
+
| Audit and rewrite an Instagram PROFILE: bio, search NAME, link, highlights, pinned posts | `wizz-ig-profile-optimizer` (social registry) | 2 |
|
|
87
89
|
| Actually PUBLISH/schedule to social networks (Instagram, TikTok, YouTube, LinkedIn and more), not write it | CLI `buffer` (social registry; needs `BUFFER_API_KEY`; media must be a public HTTPS URL). Separates "generate" (Wizz skills above) from "publish" | 2 |
|
|
88
90
|
| Cold email, B2B outbound, prospecting | `wizz-cold-email` | 2 |
|
|
89
91
|
| Referral, referrals, affiliate program | `wizz-referral-program` | 2 |
|
|
@@ -10,6 +10,7 @@ A catalogue of effects is easy. What makes a video look professional is the **re
|
|
|
10
10
|
## Where it sits in the video pipeline
|
|
11
11
|
|
|
12
12
|
1. Script and narration exist; `ctc-align` gave word-by-word timestamps.
|
|
13
|
+
On a music-driven piece (lyric video over a song) the song is the narration: `scenes[]` start on the downbeats from `wizz-video-styles/scripts/beat_grid.py`, and the song's own hits count as the SFX budget already spent.
|
|
13
14
|
2. Screens were drawn silent (HyperFrames) or footage was sourced.
|
|
14
15
|
3. **This skill:** events → SFX and levels → effects anchored on breaths → budget → timeline JSON → (render in Remotion) → gates on the final MP4.
|
|
15
16
|
4. Remotion assembles from the timeline JSON. For the `<Audio>` and `<Sequence>` API, invoke the `wizz-remotion-best-practices` skill.
|
|
@@ -20,6 +20,8 @@ It can also run at step 1, so the script is written for the style (room for b-ro
|
|
|
20
20
|
|
|
21
21
|
## Per piece
|
|
22
22
|
|
|
23
|
+
Scripts live in this skill's `scripts/`; run them from the project root (the history file is written in the current directory, or in `--project <dir>`).
|
|
24
|
+
|
|
23
25
|
1. **Inventory first, always.** Ask or read from the plan, one line each:
|
|
24
26
|
- Is a real person on camera? Which shots exist (face to camera, podcast, stage, street, hands only)?
|
|
25
27
|
- Voice: the person's own recording, a podcast/interview track, TTS, or none?
|
|
@@ -27,16 +29,17 @@ It can also run at step 1, so the script is written for the style (room for b-ro
|
|
|
27
29
|
- B-roll or stock, a physical product, food, a place?
|
|
28
30
|
- Niche, goal of the piece (follow, comment, save, click), duration.
|
|
29
31
|
Never pick a style the material cannot feed. "Talking head" needs a person on camera; "motion editorial" needs nothing but a voice; "macro / ASMR" needs a physical product filmed close.
|
|
30
|
-
2. **Pick the style** with [references/style-picker.md](references/style-picker.md): material decides which styles are possible, niche and goal rank them. Mixed is allowed and common: person on camera for the hook and the CTA, another style in the middle.
|
|
32
|
+
2. **Pick the style** with [references/style-picker.md](references/style-picker.md): material decides which styles are possible, niche and goal rank them. Mixed is allowed and common: person on camera for the hook and the CTA, another style in the middle. Before deciding, run `node scripts/references.mjs history` in the project: if the last two pieces used the same style, do not pick it a third time unless the user wants a series; choose another style the material allows, or keep it only for the hook and CTA.
|
|
31
33
|
3. **Open the style card** in `references/styles/` and follow it: rhythm, captions, palette, typography, motion, music, SFX, hook, CTA, and its "do not" list.
|
|
32
|
-
4. **
|
|
34
|
+
4. **Get fresh references and watch two** before editing: `node scripts/references.mjs pick --style <key>` returns measured references plus, for styles generated in code, current viral videos from the awesome-opus5-5-videos list, skipping everything this project already used. Reading a card is not watching a video. For how those generated videos were briefed, see [references/generated-prompts.md](references/generated-prompts.md).
|
|
33
35
|
5. **Audio:** music and SFX choices from [references/audio.md](references/audio.md); the redistributable kit is in `assets/audio/` (credits in `assets/audio/CREDITS.md`).
|
|
34
36
|
6. **Hand over** to `wizz-video-effects` with the card's SFX ceiling and music level, then to Remotion.
|
|
35
37
|
7. **Look at the final file**: extract frames and compare them side by side with a contact sheet of one reference.
|
|
38
|
+
8. **Record the piece**: `node scripts/references.mjs record --style <key> --piece <slug> --refs <ids watched>`. It writes `_wizz-output/video-styles/historico.json`, which is what keeps the next piece from repeating the same references and look.
|
|
36
39
|
|
|
37
40
|
## What the measurements say across every style
|
|
38
41
|
|
|
39
|
-
- **SFX are rare.** Across
|
|
42
|
+
- **SFX are rare.** Across 104 measured shorts, sound effects landed on a cut in a small minority of cuts (per-style counts in each card). Putting a sound on every cut is the most common amateur tell.
|
|
40
43
|
- **Music bed: style-dependent.** Fast montage, animated explainers and travel POV carry a continuous bed; talking heads and podcast clips mostly have none or a faint one.
|
|
41
44
|
- **Captions** in reach-heavy styles are 1 to 3 words at a time, bold, lower-centre, with one keyword in an accent colour; editorial and explainer styles keep them small or drop them.
|
|
42
45
|
- **Rhythm is a style property, not a virtue.** The highest-reach videos in the set range from single takes with no detected cut to a cut every second.
|
|
@@ -53,7 +56,10 @@ Details and the per-style table: [references/measured-patterns.md](references/me
|
|
|
53
56
|
| [references/audio.md](references/audio.md) | music and SFX per style, the kit, libraries and licences |
|
|
54
57
|
| [references/research-method.md](references/research-method.md) | how the catalogue was measured, its limits, how to add a niche or a style |
|
|
55
58
|
| `data/references.json` | every measured short: link, channel, views, niche, style, metrics |
|
|
59
|
+
| [references/generated-prompts.md](references/generated-prompts.md) | prompts of viral code-generated videos, grouped by style, and how they run here |
|
|
56
60
|
| `scripts/analyze.sh` | measures one short (cuts, music bed, SFX heuristics, loudness, contact sheet) |
|
|
61
|
+
| `scripts/references.mjs` | fresh references per style without repeats, and the project's history of styles used |
|
|
62
|
+
| `scripts/beat_grid.py` | BPM, beats and downbeats of a song, so cuts snap to the music (lyric video, kinetic typography) |
|
|
57
63
|
| `assets/remotion/editorial/` | Remotion template of the motion editorial style |
|
|
58
64
|
| `assets/audio/` | licensed music beds and SFX that may ship with a project |
|
|
59
65
|
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
"e": "health / fitness / nutrition",
|
|
8
8
|
"f": "food / recipes / lifestyle / travel",
|
|
9
9
|
"g": "beauty / fashion / local services (clinic, salon, architecture, real estate)",
|
|
10
|
-
"h": "education / science / comedy"
|
|
10
|
+
"h": "education / science / comedy",
|
|
11
|
+
"i": "music / artists / lyric videos"
|
|
11
12
|
},
|
|
12
13
|
"styles": {
|
|
13
14
|
"motion-editorial": {
|
|
@@ -336,6 +337,37 @@
|
|
|
336
337
|
"dont": [
|
|
337
338
|
"Setup longer than 3 s before the first joke."
|
|
338
339
|
]
|
|
340
|
+
},
|
|
341
|
+
"tipografia-cinetica": {
|
|
342
|
+
"title": "Kinetic typography and lyric video (mexicat P(doom), PixelValet, Ninh Truong)",
|
|
343
|
+
"needs": "An audio track with words: a song with its lyrics, or a voice-over with its script. Nobody on camera, no footage. Everything is generated (HyperFrames, three.js, canvas). Word timing from ctc-align; a beat grid when there is music.",
|
|
344
|
+
"fits": "Music releases and lyric videos, manifestos, a strong opinion read aloud, brand promos built on a slogan, poetry. Shares and rewatches; weak for teaching step by step.",
|
|
345
|
+
"look": [
|
|
346
|
+
"The words ARE the image: one keyword huge in the frame, the rest of the line small. No caption strip at the bottom.",
|
|
347
|
+
"Plain background (black, or a flat colour that changes per section) or one generated world the camera travels through (a map, a grid in perspective, a 3D object the line wraps around).",
|
|
348
|
+
"Each word is animated by what it means: PAIN breaks, CHEERFUL bounces, LIGHT lights up, a word curves around the object it names.",
|
|
349
|
+
"Code-rendered pieces add film treatment: bloom, halation, grain, a thin HUD in mono."
|
|
350
|
+
],
|
|
351
|
+
"palette": "Dark base (#0A0A0A or a deep teal #0F3B3A) plus ONE glowing accent (orange #FF6A1A in P(doom), coral in Maps) and off-white text. Flat-colour variant: one saturated background per word or section, text in white or ink.",
|
|
352
|
+
"type": "Two or three families with high contrast: a heavy grotesk or condensed caps for the keyword (Archivo Black, Bebas Neue), a mono for small lines and HUD (IBM Plex Mono), optionally a serif italic for a soft word (Cormorant Garamond). Mix weights inside the same line.",
|
|
353
|
+
"motion": [
|
|
354
|
+
"Words enter on the syllable they are sung or spoken (forced alignment, never guessed); the keyword lands on the beat.",
|
|
355
|
+
"Scene changes snap to the downbeat. P(doom) changes every ~0.3 s at peaks; the calm variant is a single continuous camera move for a whole verse.",
|
|
356
|
+
"The camera moves through the type (push, orbit, travel across a map) instead of the text sliding over a still frame.",
|
|
357
|
+
"Hold the last word of a line a beat longer before it leaves."
|
|
358
|
+
],
|
|
359
|
+
"captions": "None separate: the type is the caption. Keep the full line readable somewhere (small, mono, corner) when the keyword alone would lose the sense.",
|
|
360
|
+
"hook": "The first sung or spoken word, huge, in the accent colour, landing on the first downbeat (0 to 1 s). No logo intro.",
|
|
361
|
+
"cta": "The title or slogan locked up big and still for 1 to 2 s, the accent lit; one small line (artist, handle, link) under it.",
|
|
362
|
+
"music": "The track itself is the whole soundtrack. Lyric videos: song at full level, no extra bed. Voice-over promos: a beat-driven bed under the voice.",
|
|
363
|
+
"sfx": "Song-driven pieces: almost none (5 of 56 cuts in P(doom), 0 of 2 in Maps). Promo variant: hits on every word change (9/13 and 16/20 cuts), which the heuristic partly confuses with the kick. Never add whooshes over a song.",
|
|
364
|
+
"dont": [
|
|
365
|
+
"Karaoke subtitles at the bottom over a static background: that is a lyric 'overlay', not this style.",
|
|
366
|
+
"Guessing word timing by ear; align it (ctc-align) and snap scenes to the beat grid.",
|
|
367
|
+
"More than one accent colour, or a different font for every word.",
|
|
368
|
+
"Shipping 16:9: every measured reference is horizontal; recompose for 9:16 with the keyword in the upper-middle and nothing in the bottom 20%."
|
|
369
|
+
],
|
|
370
|
+
"template": null
|
|
339
371
|
}
|
|
340
372
|
}
|
|
341
373
|
}
|