/*  primitivas.jsx — as primitivas de cena do estudio_motion (ES3), além dos 8 tipos de cenas.jsx.

    Vocabulário tirado das 68 receitas do Clube (identidade, montagem e decisões) e dos scripts AE dos 5 projetos:
    interface recriada em camadas, cena que vira a próxima, cadeia de câmera com nulls, tipografia cinética,
    contador, gráfico que cresce, mapa com pino, card que pula, cursor que conduz, notificação, chat, linha do
    tempo, laço de processo, explosão de ícones, máscara que revela, split antes/depois, objeto 3D falso com
    sombra, partículas, relógio, calendário, funil, parede de logos, prompt, odômetro, medidor, carimbo, leque,
    ondas, rolagem, prova social.

    Todas leem o mesmo conteúdo (titulo, sub, itens, valor, rotulo, antes, depois, texto, autor, botao) e usam
    os ELEMENTOS REAIS do assunto (KIT.elementos). Posições são frações do quadro, então valem em 9:16, 1:1 e 16:9.
*/

function kPre(comp, P) { var g = { W: comp.width, H: comp.height, u: kU(comp), v: kVert(comp), cam: kBase(comp, P) }; g.cx = g.W / 2; g.cy = g.H / 2; return g; }
function kSobre(P) { return kLum(P.ac) > 0.5 ? '#14110F' : '#FFFFFF'; }
/* O cartão em destaque vem da paleta da cena (maquina.js, cartaoDestaque): o acento quando é cor; senão o brilho. Nunca preto solto. */
function kCardAc(P) { return P.cardAc || P.ac; }
function kSobreCard(P) { return P.sobreCardAc || kSobre(P); }
function kT(g, comp, P, str, y, o) {
    o = o || {};
    var t0 = o.t0 != null ? o.t0 : 0.14, W = g.W, H = g.H;
    var topo = g.v && y < H * 0.5;   // título de cabeçalho: vai para o topo da zona segura, em NO MÁXIMO 2 linhas, e o CONTEÚDO desce para baixo da faixa dele
    var r = texto(comp, str, { tamanho: (o.tam || (g.v ? 96 : 84)) * g.u, cor: o.cor || P.fg, corDestaque: o.corD || P.ac, x: o.x == null ? g.cx : o.x, y: topo ? 0 : y, parent: topo ? (g.cam.camNo || g.cam.no) : g.cam.no, entrada: o.entrada, t0: Q(str, t0), largura: o.largura || g.W * (g.v ? 0.86 : 0.74), maxLinhas: topo ? 2 : (o.maxLinhas || 3), sublinhado: o.sublinhado, fonte: o.fonte, sobrepoe: o.sobrepoe, passo: o.passo, entreLinhas: o.entreLinhas, livre: o.livre, principal: o.principal !== false, titulo: true });
    if (o.som !== false && r.tempos.length) { som(r.tempos[0], 'pop', 0.6); if (r.tempos.length > 2) som(r.tempos[r.tempos.length - 1], 'tique', 0.4); }
    if (topo && r.camadas.length) {
        var zl = kZonaTexto(comp, true), Ct = o.ct != null ? o.ct : H * 0.2, Cb = H - zl.bot, B = r.y + r.h / 2 + 24;   // B = base da faixa reservada (caixa + 24 px)
        KIT.tituloB = B;
        KIT.rects.push({ espaco: 't', banda: true, cena: KIT.cenaIdx, x0: r.x - r.w / 2 - 24, x1: r.x + r.w / 2 + 24, y0: r.y - r.h / 2 - 24, y1: B, t0: 0, t1: 1, texto: false, nome: 'faixa do título' });
        if (B > Ct + 2) {
            var s = Math.max(0.7, Math.min(1, (Cb - B) / (Cb - Ct)));
            KIT.remap = { s: s, px: g.cx, py: B, ax: g.cx, ay: Ct };
            var cn = g.cam.no; kA(cn).setValue([g.cx, Ct]); kP(cn).setValue([g.cx, B]); kS(cn).setValue([s * 100, s * 100]);
        }
    }
    return r;
}
/* Os itens da lista, sem repetir (o mesmo texto, ignorando caixa, acento e espaço, entra uma vez só): faltou item, a lista fica menor. */
function kChaveItem(s) { return String(s || '').toLowerCase().replace(/[\*\s.,;:!?]+/g, '').replace(/[áàâã]/g, 'a').replace(/[éê]/g, 'e').replace(/[í]/g, 'i').replace(/[óôõ]/g, 'o').replace(/[ú]/g, 'u').replace(/ç/g, 'c'); }
function kItens(d, padrao, n) {
    var fonte = d.itens && d.itens.length ? d.itens : padrao, it = [], visto = {}, i;
    for (i = 0; i < fonte.length && it.length < (n || 5); i++) { var k = kChaveItem(fonte[i]); if (!k || visto[k]) continue; visto[k] = 1; it.push(fonte[i]); }
    return it;
}
function kEstrelaPts(r, ri) { var p = [], i; for (i = 0; i < 10; i++) { var a = -Math.PI / 2 + i * Math.PI / 5, rr = i % 2 ? ri : r; p.push([Math.cos(a) * rr, Math.sin(a) * rr]); } return p; }
function kContr(a, b) { var x = kLum(a) + 0.05, y = kLum(b) + 0.05; return x > y ? x / y : y / x; }
function kEsc(s) { return String(s).replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\r?\n/g, ' '); }
/* digita(comp, str, {x (borda esquerda), y, tamanho, fonte, cor, largura, parent, t0, cps}): texto que se escreve com cursor piscando. */
function digita(comp, str, o) {
    var tam = o.tamanho || 40, w = kMedir(comp, str, o.fonte, tam);
    while (w > o.largura && tam > 18) { tam *= 0.93; w = kMedir(comp, str, o.fonte, tam); }
    var L = comp.layers.addText(str); L.name = 'digitando'; if (o.parent) kPai(L, o.parent);
    var td = kDoc(L, o.fonte, tam, o.cor || '#111111', { just: 'left' });
    kP(L).setValue([o.x, o.y + tam * 0.33]);
    var cps = o.cps || 16, t0 = o.t0, n = str.length;
    td.expression = 'var s = "' + kEsc(str) + '"; var n = Math.floor(Math.max(0, time - ' + t0.toFixed(3) + ') * ' + cps + '); s.substr(0, Math.min(s.length, n)) + ((time > ' + t0.toFixed(3) + ' && Math.floor(time * 2.4) % 2 == 0 && n < s.length + 10) ? "|" : "")';
    var k, ns = Math.min(10, Math.ceil(n / 3));
    for (k = 0; k < ns; k++) som(t0 + (k * 3) / cps, 'digita', 0.55);
    return { L: L, w: w, tam: tam, fim: t0 + n / cps };
}
function kFundoSimples(comp, P) { fundo(comp, P.bg, {}); return camera(comp); }

// ---------------------------------------------------------------- 1. cinético: uma palavra por vez, cada uma entra de um jeito
function cenaCinetico(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam;
    var pal = kPalavras(d.titulo || d.texto || ''), n = pal.length, i;
    var modos = ['palavra', 'voa', 'mascara', 'sobe', 'voa', 'palavra'];
    var passo = Math.max(0.28, Math.min(0.62, (comp.duration - 0.5) / (n + 0.6)));
    var disco = forma(comp, 'circulo', { x: g.cx, y: g.cy, w: Math.min(W, H) * 0.9, cor: P.card, parent: cam.no, nome: 'disco' });
    for (i = 0; i < n; i++) {
        var ti = Q(pal[i].t, 0.14 + i * passo), fim = i < n - 1 ? Math.max(ti + 0.22, Q(pal[i + 1].t, 0.14 + (i + 1) * passo) - 0.02) : comp.duration;
        // a palavra tem que ficar INTEIRA e legível antes de sair (0,15 s de verdade: a cena nominal toca f× mais rápido); a letra
        // que voa só entra se dá tempo de montar e ler
        var modo = kEntradaQueCabe(modos[i % modos.length], pal[i].t, (i < n - 1 ? fim : comp.duration) - ti, 0.15 * (KIT.f || 1));
        var r = texto(comp, (pal[i].kw ? '*' : '') + pal[i].t, { tamanho: (g.v ? 230 : 200) * u, cor: i % 3 == 2 ? P.ac : P.fg, corDestaque: P.ac, x: g.cx, y: g.cy, parent: cam.no, entrada: modo, t0: ti, largura: W * 0.84, maxLinhas: 1, sublinhado: false, fala: false, sobrepoe: true });
        var j; for (j = 0; j < r.camadas.length; j++) if (i < n - 1) curva(kO(r.camadas[j]), [[fim, 100, null, 30], [fim + 0.06, 0, 60, null]]);
        mola(kS(disco), ti, [88, 88], [100, 100], { quique: 0.5, dur: 0.45 });
        som(ti, i % 2 ? 'tique' : 'pop', 0.55);
    }
    som(0.1, 'sobe', 0.5);
    cam.empurra(0.6, 0, 0, 1.0).empurra(comp.duration - 0.2, 0, 0, 1.1);
    fecha(comp);
}

// ---------------------------------------------------------------- 2. interface recriada em camadas (paralaxe)
function cenaInterface(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12;
    var ww = v ? W * 0.8 : W * 0.56, wh = v ? H * 0.46 : H * 0.72, wx = v ? g.cx : W * 0.66, wy = v ? H * 0.58 : g.cy;
    if (d.titulo) kT(g, comp, P, d.titulo, v ? H * 0.17 : g.cy, { x: v ? g.cx : W * 0.22, largura: v ? W * 0.86 : W * 0.34, tam: v ? 84 : 76, maxLinhas: 4 });
    var win = grupo(comp, { x: wx, y: wy, parent: cam.no, nome: 'janela' });
    var corpo = forma(comp, 'retangulo', { x: 0, y: 0, w: ww, h: wh, r: 38 * u, cor: P.card, traco: P.mudo, tracoL: 3 * u, parent: win, nome: 'janela-corpo', entrada: 'cresce', t0: T0 + 0.1 });
    var bar = forma(comp, 'retangulo', { x: 0, y: -wh / 2 + 34 * u, w: ww, h: 68 * u, r: 38 * u, cor: P.bg, alfa: 60, parent: win, nome: 'barra', entrada: 'fade', t0: T0 + 0.25 });
    var i, cores = ['#FF5F57', '#FEBC2E', '#28C840'];
    for (i = 0; i < 3; i++) forma(comp, 'circulo', { x: -ww / 2 + (36 + i * 34) * u, y: -wh / 2 + 34 * u, w: 20 * u, cor: cores[i], parent: win, entrada: 'pop', t0: T0 + 0.3 + i * 0.05, nome: 'ponto' });
    // camada do fundo (sidebar) e camada da frente (cartões): se movem em velocidades diferentes
    var lado = grupo(comp, { x: 0, y: 0, parent: win, nome: 'camada-lado' }), frente = grupo(comp, { x: 0, y: 0, parent: win, nome: 'camada-frente' });
    kP(lado).expression = 'value + [Math.sin(time*0.9)*5, Math.cos(time*0.7)*4]';
    kP(frente).expression = 'value + [Math.sin(time*0.9+1)*-11, Math.cos(time*0.7+1)*-8]';
    var sw = ww * 0.22, sx = -ww / 2 + sw / 2 + 18 * u;
    var sb = forma(comp, 'retangulo', { x: sx, y: 34 * u, w: sw, h: wh - 140 * u, r: 24 * u, cor: P.bg, alfa: 55, parent: lado, nome: 'sidebar' });
    curva(kP(sb), [[T0 + 0.3, [sx - 80 * u, 34 * u], null, 25], [T0 + 0.8, [sx, 34 * u], 85, null]]);
    var it = kItens(d, ['Início', 'Clientes', 'Vendas'], 4), n = it.length;
    for (i = 0; i < n; i++) {
        var ly = -wh / 2 + 150 * u + i * 70 * u, tl = Q(it[i], T0 + 0.6 + i * 0.22);
        var pill = forma(comp, 'pilula', { x: sx, y: ly, w: sw * 0.82, h: 46 * u, cor: i == 0 ? P.ac : P.mudo, alfa: i == 0 ? 100 : 30, parent: lado, entrada: 'pop', t0: tl, nome: 'menu' });
        texto(comp, it[i], { tamanho: 26 * u, cor: i == 0 ? kSobre(P) : P.fg, x: sx, y: ly, parent: lado, entrada: 'sobe', t0: tl + 0.06, largura: sw * 0.72, maxLinhas: 1, sublinhado: false, livre: true, fala: false });
        som(tl, 'tique', 0.45);
    }
    var cx0 = sx + sw / 2 + 24 * u, cwid = ww / 2 - cx0 - 22 * u, k, vals = [d.valor || '+128', '4,9', '32'];
    for (k = 0; k < 3; k++) {
        var cw = (cwid - 2 * 18 * u) / 3, kx = cx0 + cw / 2 + k * (cw + 18 * u), ky = -wh / 2 + 160 * u, tk = Q(vals[k], T0 + 1.0 + k * 0.2);
        var cd = forma(comp, 'retangulo', { x: kx, y: ky, w: cw, h: 130 * u, r: 24 * u, cor: k == 0 ? kCardAc(P) : P.bg, alfa: k == 0 ? 100 : 70, parent: frente, entrada: 'pop', t0: tk, quique: 0.6, nome: 'card' });
        texto(comp, String(vals[k]), { fonte: P.fonteTitulo, tamanho: 46 * u, cor: k == 0 ? kSobreCard(P) : P.fg, x: kx, y: ky - 6 * u, parent: frente, entrada: 'palavra', t0: tk + 0.08, largura: cw * 0.86, maxLinhas: 1, sublinhado: false, livre: true, fala: false });
        som(tk, 'pop', 0.5);
    }
    // gráfico de barras dentro da janela
    var by = wh / 2 - 60 * u, nb = 7;
    for (i = 0; i < nb; i++) {
        var bh = (60 + ((i * 53) % 90) + i * 14) * u, bw = (cwid - (nb - 1) * 12 * u) / nb, bx = cx0 + bw / 2 + i * (bw + 12 * u), tb = T0 + 1.5 + i * 0.07;
        var b = forma(comp, 'retangulo', { x: bx, y: by, w: bw, h: bh, r: 8 * u, cor: i == nb - 1 ? P.ac : P.mudo, alfa: i == nb - 1 ? 100 : 55, parent: frente, nome: 'barra-grafico' });
        kA(b).setValue([0, bh / 2]); kP(b).setValue([bx, by + bh / 2 - bh / 2 + bh / 2 - bh / 2]);
        kP(b).setValue([bx, by]);
        mola(kS(b), tb, [100, 0], [100, 100], { quique: 0.5, dur: 0.6 });
    }
    som(T0 + 1.5, 'sobe', 0.45);
    mola(kS(win), T0 + 0.1, [78, 78], [100, 100], { quique: 0.4, dur: 0.8 });   // depois dos filhos: o EffectCraft compensa a escala do pai no parentesco
    cam.empurra(0.8, 0, 0, 1.0).empurra(T0 + 1.8, v ? 0 : -60 * u, v ? 70 * u : 0, 1.08).empurra(comp.duration - 0.2, v ? 0 : -90 * u, v ? 110 * u : 0, 1.12);
    fecha(comp);
}

// ---------------------------------------------------------------- 3. mergulho: a tela do celular vira a próxima cena
function cenaMergulho(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12, dur = comp.duration, alvo = d.proxBg || P.ac;
    var esc = v ? 0.62 * H / 960 * 0.92 : 0.7 * H / 960;
    if (d.titulo) kT(g, comp, P, d.titulo, v ? H * 0.14 : g.cy, { x: v ? g.cx : W * 0.26, largura: v ? W * 0.86 : W * 0.4, tam: v ? 92 : 84, maxLinhas: 3 });
    var px = v ? g.cx : W * 0.7, py = v ? H * 0.58 : g.cy;
    var cel = celular(comp, { x: px, y: py, escala: esc, parent: cam.no, t0: T0 + 0.1, tela: alvo });
    var corTxt = kLum(alvo) > 0.5 ? '#14110F' : '#FFFFFF';
    var pr = d.prox || d.rotulo || d.sub || '';
    if (pr) texto(comp, pr, { tamanho: 58, cor: corTxt, corDestaque: corTxt, x: 0, y: -40, parent: cel.no, entrada: 'palavra', t0: T0 + 0.9, largura: 360, maxLinhas: 4, sublinhado: false, livre: true, fala: false });
    var el = elemento(comp, 0, { x: 0, y: 200, w: 190, parent: cel.no, entrada: 'pop', t0: T0 + 0.7, flutua: 8 });
    som(T0 + 0.7, 'pop', 0.6);
    var tm = dur - 1.0, dedo = null;
    cel.toque(tm - 0.5, 0, 200, { cor: corTxt });
    // o mergulho: o celular cresce em volta do centro da tela até preencher o quadro
    var e0 = esc * 100, kk = Math.max(W, H) / 380 * 100 * 1.15;
    curva(kS(cel.no), [[tm, [e0, e0], null, 14], [tm + 0.75, [e0 * 4, e0 * 4], 30, 40], [tm + 0.98, [kk * 1.6, kk * 1.6], 45, null]]);
    curva(kP(cel.no), [[tm, [px, py], null, 20], [tm + 0.75, [g.cx, g.cy], 70, null]]);
    som(tm, 'sobe', 0.7); som(tm + 0.7, 'troca', 0.7);
    var tampa = comp.layers.addSolid(kCor(alvo), 'tampa', W, H, 1); tampa.parent = null;
    curva(kO(tampa), [[tm + 0.6, 0, null, 30], [tm + 0.92, 100, 70, null]]);
    cam.empurra(0.8, 0, 0, 1.02);
    fecha(comp);
}

// ---------------------------------------------------------------- 4. câmera em cadeia de nulls: painéis numa tela grande
function cenaCameraNulls(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12;
    var it = kItens(d, ['Primeiro', 'Segundo', 'Terceiro'], 4), n = it.length, i;
    var mundo = grupo(comp, { x: g.cx, y: g.cy, parent: cam.no, nome: 'pan' });   // null 1: o deslocamento
    var zoom = grupo(comp, { x: 0, y: 0, parent: mundo, nome: 'zoom' });           // null 2: o respiro de escala
    var pw = v ? W * 0.74 : W * 0.5, ph = v ? H * 0.5 : H * 0.62, gap = v ? H * 0.62 : W * 0.6;
    var ts = [], tot = comp.duration - 0.5, passo = Math.max(0.55, (tot - 0.4) / n);
    for (i = 0; i < n; i++) {
        var ox = v ? 0 : i * gap, oy = v ? i * gap : 0, ti = Q(it[i], 0.3 + i * passo);
        ts.push(ti);
        var c = forma(comp, 'retangulo', { x: ox, y: oy, w: pw, h: ph, r: 56 * u, cor: i % 2 ? P.card : kCardAc(P), parent: zoom, nome: 'painel' });
        var tc = i % 2 ? P.fg : kSobreCard(P), ta = i % 2 ? P.ac : kSobreCard(P);
        texto(comp, String(i + 1), { fonte: P.fonteTitulo, tamanho: 220 * u, cor: ta, corDestaque: ta, x: ox - pw * 0.28, y: oy - ph * 0.25, parent: zoom, entrada: 'palavra', t0: 0.3, largura: pw * 0.4, maxLinhas: 1, sublinhado: false, livre: true, fala: false });
        elemento(comp, i, { x: ox + pw * 0.25, y: oy - ph * 0.22, w: pw * 0.38, parent: zoom, flutua: 8, fase: i });
        texto(comp, it[i], { tamanho: 70 * u, cor: tc, corDestaque: ta, x: ox, y: oy + ph * 0.22, parent: zoom, entrada: 'mascara', t0: 0.4, largura: pw * 0.82, maxLinhas: 3, sublinhado: false, livre: true, fala: false });
    }
    // a câmera viaja: cada parada é uma mola do null de deslocamento, com o null de zoom respirando no caminho
    for (i = 0; i < n; i++) {
        var tt = i == 0 ? 0.05 : Math.max(0.5, ts[i] - 0.35), alvoX = v ? g.cx : g.cx - i * gap, alvoY = v ? g.cy - i * gap : g.cy;
        if (i == 0) kP(mundo).setValue([alvoX, alvoY]);
        else {
            var de = kP(mundo).valueAtTime(tt, false);
            mola(kP(mundo), tt, [v ? g.cx : g.cx - (i - 1) * gap, v ? g.cy - (i - 1) * gap : g.cy], [alvoX, alvoY], { quique: 0.3, dur: 0.75 });
            curva(kS(zoom), [[tt, [100, 100], null, 25], [tt + 0.3, [78, 78], 60, 40], [tt + 0.75, [100, 100], 70, null]]);
            som(tt, 'troca', 0.6);
        }
        som(tt + 0.5, 'pop', 0.5);
    }
    cam.empurra(0.8, 0, 0, 1.0).empurra(comp.duration - 0.2, 0, 0, 1.06);
    fecha(comp);
}

// ---------------------------------------------------------------- 5. gráfico de barras que cresce
function cenaBarras(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12;
    var it = kItens(d, ['Antes', 'Mês 1', 'Mês 2', 'Agora'], 5), n = it.length, i;
    var tt = kT(g, comp, P, d.titulo || '', v ? H * 0.16 : H * 0.17, { maxLinhas: 2, tam: v ? 88 : 76 });
    var area = [W * 0.2, H * 0.3, W * 0.8, H * 0.78], aw = area[2] - area[0], ah = area[3] - area[1];
    var bw = Math.min(aw / n * 0.62, 190 * u), gap = aw / n, i0;
    var base = forma(comp, 'retangulo', { x: g.cx, y: area[3] + 6 * u, w: aw + 30 * u, h: 8 * u, cor: P.mudo, parent: cam.no, entrada: 'fade', t0: T0, nome: 'base' });
    var tops = [];
    for (i = 0; i < n; i++) {
        var altura = ah * (0.2 + 0.8 * Math.pow((i + 1) / n, 1.5)), bx = area[0] + gap * (i + 0.5), ti = Q(it[i], T0 + 0.3 + i * 0.4), ult = i == n - 1;
        var b = forma(comp, 'retangulo', { x: bx, y: area[3], w: bw, h: altura, r: 18 * u, cor: ult ? kCardAc(P) : P.card, traco: ult ? null : P.mudo, tracoL: 3 * u, parent: cam.no, nome: 'barra' });
        kA(b).setValue([0, altura / 2]);
        mola(kS(b), ti, [100, 0], [100, 100], { quique: ult ? 0.7 : 0.4, dur: 0.7 });
        som(ti, ult ? 'impacto' : 'pop', ult ? 0.8 : 0.55);
        texto(comp, it[i], { opcional: true, fonte: P.fonteTexto, tamanho: 34 * u, cor: P.mudo, x: bx, y: area[3] + 52 * u, parent: cam.no, entrada: 'sobe', t0: ti + 0.1, largura: gap * 0.92, maxLinhas: 2, sublinhado: false, fala: false });
        tops.push([bx, area[3] - altura]);
    }
    var vt = d.valor || '';
    if (vt) {
        var tp = Q(vt, T0 + 0.3 + n * 0.4 + 0.2), bub = forma(comp, 'pilula', { x: tops[n - 1][0], y: tops[n - 1][1] - 70 * u, w: 250 * u, h: 100 * u, cor: P.fg, parent: cam.no, entrada: 'pop', t0: tp, quique: 0.7, nome: 'valor' });
        texto(comp, vt, { fonte: P.fonteTitulo, tamanho: 58 * u, cor: P.bg, corDestaque: P.bg, x: tops[n - 1][0], y: tops[n - 1][1] - 70 * u, parent: cam.no, entrada: 'palavra', t0: tp + 0.06, largura: 220 * u, maxLinhas: 1, sublinhado: false, livre: true, fala: false });
        som(tp, 'brilho', 0.6);
    }
    var pts = [[tops[0][0], tops[0][1] - 40 * u], [tops[n - 1][0] - bw * 0.3, tops[n - 1][1] - 40 * u]], tl = T0 + 0.3 + n * 0.4;
    caminho(comp, [[tops[0][0], tops[0][1] - 40 * u], [tops[Math.floor(n / 2)][0], tops[Math.floor(n / 2)][1] - 50 * u], [tops[n - 1][0], tops[n - 1][1] - 40 * u]], { cor: P.ac, esp: 8 * u, t0: tl, dur: 0.7, parent: cam.no, suave: true, nome: 'tendencia' });
    cam.empurra(0.8, 0, 0, 1.0).empurra(comp.duration - 0.2, 0, -20 * u, 1.08);
    fecha(comp);
}

// ---------------------------------------------------------------- 6. gráfico de linha que se desenha
function cenaLinha(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12, i;
    kT(g, comp, P, d.titulo || '', v ? H * 0.16 : H * 0.17, { maxLinhas: 2, tam: v ? 88 : 76 });
    var x0 = W * 0.13, x1 = W * 0.87, y0 = v ? H * 0.34 : H * 0.34, y1 = v ? H * 0.68 : H * 0.8, N = 7;
    for (i = 0; i < 4; i++) { var gy = y0 + (y1 - y0) * i / 3; forma(comp, 'retangulo', { x: g.cx, y: gy, w: x1 - x0, h: 3 * u, cor: P.mudo, alfa: 35, parent: cam.no, entrada: 'fade', t0: T0 + i * 0.06, nome: 'grade' }); }
    var vs = [0.12, 0.2, 0.17, 0.38, 0.5, 0.46, 0.88], pts = [], area = [];
    for (i = 0; i < N; i++) pts.push([x0 + (x1 - x0) * i / (N - 1), y1 - (y1 - y0) * vs[i]]);
    var tl = T0 + 0.45, dd = 1.3;
    var ar = pts.slice(0); ar.push([x1, y1]); ar.push([x0, y1]);
    var fill = caminho(comp, ar, { preenche: P.ac, fecha: true, parent: cam.no, alfa: 0, nome: 'area' });
    curva(kO(fill), [[tl + dd * 0.6, 0, null, 30], [tl + dd + 0.4, 22, 60, null]]);
    caminho(comp, pts, { cor: P.ac, esp: 14 * u, t0: tl, dur: dd, parent: cam.no, suave: true, nome: 'linha' });
    for (i = 0; i < N; i++) {
        var tp = tl + dd * i / (N - 1);
        forma(comp, 'circulo', { x: pts[i][0], y: pts[i][1], w: (i == N - 1 ? 46 : 28) * u, cor: i == N - 1 ? P.ac : P.bg, traco: P.ac, tracoL: 7 * u, parent: cam.no, entrada: 'pop', t0: tp, quique: 0.6, nome: 'ponto' });
        if (i % 2 == 0) som(tp, 'ponto' + (1 + i % 5), 0.5);
    }
    var vt = d.valor || '+', te = Q(vt, tl + dd);
    var ult = pts[N - 1];
    forma(comp, 'pilula', { x: ult[0] - 150 * u, y: ult[1] - 90 * u, w: 280 * u, h: 104 * u, cor: P.fg, parent: cam.no, entrada: 'pop', t0: te, quique: 0.7, nome: 'valor' });
    texto(comp, vt, { fonte: P.fonteTitulo, tamanho: 62 * u, cor: P.bg, corDestaque: P.bg, x: ult[0] - 150 * u, y: ult[1] - 90 * u, parent: cam.no, entrada: 'palavra', t0: te + 0.06, largura: 250 * u, maxLinhas: 1, sublinhado: false, livre: true, fala: false });
    som(tl, 'sobe', 0.5); som(te, 'brilho', 0.7);
    if (d.rotulo) texto(comp, d.rotulo, { fonte: P.fonteTexto, tamanho: 46 * u, cor: P.mudo, x: g.cx, y: y1 + 84 * u, parent: cam.no, entrada: 'sobe', t0: Q(d.rotulo, te + 0.2), largura: W * 0.8, maxLinhas: 2, sublinhado: false });
    cam.empurra(0.8, 0, 0, 1.0).empurra(tl + dd, -30 * u, -10 * u, 1.07).empurra(comp.duration - 0.2, -50 * u, -20 * u, 1.1);
    fecha(comp);
}

// ---------------------------------------------------------------- 7. mapa com pinos que caem
function cenaMapa(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12, i;
    var mapa = grupo(comp, { x: g.cx, y: g.cy, parent: cam.no, nome: 'mapa' });
    var s = Math.max(W, H) * 1.3;
    forma(comp, 'retangulo', { x: 0, y: 0, w: s, h: s, cor: P.card, parent: mapa, nome: 'terra' });
    var rio = caminho(comp, [[-s / 2, s * 0.18], [-s * 0.15, s * 0.05], [s * 0.1, s * 0.22], [s / 2, s * 0.1]], { cor: P.ac, esp: 70 * u, alfa: 35, parent: mapa, suave: true, t0: 0.05, dur: 0.8, nome: 'rio' });
    for (i = -4; i <= 4; i++) {
        caminho(comp, [[i * s * 0.11, -s / 2], [i * s * 0.11 + (i % 2) * 40 * u, s / 2]], { cor: P.mudo, esp: (i % 3 == 0 ? 16 : 8) * u, alfa: 45, parent: mapa, t0: 0.05 + Math.abs(i) * 0.03, dur: 0.6, nome: 'rua' });
        caminho(comp, [[-s / 2, i * s * 0.11], [s / 2, i * s * 0.11 - (i % 2) * 30 * u]], { cor: P.mudo, esp: (i % 3 == 0 ? 16 : 8) * u, alfa: 45, parent: mapa, t0: 0.1 + Math.abs(i) * 0.03, dur: 0.6, nome: 'rua' });
    }
    var it = kItens(d, ['Aqui', 'Perto', 'Você'], 4), n = it.length;
    for (i = 0; i < n; i++) {
        var a = i * 2.1 + 0.6, px = Math.cos(a) * W * (v ? 0.25 : 0.2) + (i - n / 2) * 20 * u, py = Math.sin(a) * H * 0.16 + (i - 1) * 30 * u + (v ? 40 * u : 0);
        var ti = Q(it[i], 0.5 + i * 0.5), pin = grupo(comp, { x: px, y: py, parent: mapa, nome: 'pino' });
        var rad = forma(comp, 'circulo', { x: 0, y: 0, w: 220 * u, traco: P.ac, tracoL: 6 * u, alfa: 0, parent: pin, nome: 'radar' });
        curva(kS(rad), [[ti + 0.3, [20, 20], null, 20], [ti + 1.2, [140, 140], 80, null]]);
        curva(kO(rad), [[ti + 0.3, 70, null, 30], [ti + 1.2, 0, 70, null]]);
        var cab = forma(comp, 'circulo', { x: 0, y: -60 * u, w: 78 * u, cor: i == n - 1 ? P.ac : P.fg, parent: pin, nome: 'pino-cabeca' });
        caminho(comp, [[-30 * u, -42 * u], [0, 0], [30 * u, -42 * u]], { preenche: i == n - 1 ? P.ac : P.fg, fecha: true, parent: pin, nome: 'pino-ponta' });
        forma(comp, 'circulo', { x: 0, y: -60 * u, w: 28 * u, cor: P.bg, parent: pin, nome: 'pino-miolo' });
        curva(kO(pin), [[ti - 0.001 > 0 ? ti - 0.04 : 0, 0, null, 20], [ti, 100, null, 30]]);
        mola(kP(pin), ti, [px, py - 420 * u], [px, py], { quique: 0.7, dur: 0.7 });
        som(ti + 0.28, 'pop', 0.7);
        var cx2 = px + (px > 0 ? -1 : 1) * 0, ly = py - 150 * u;
        forma(comp, 'pilula', { x: px, y: ly, w: Math.min(W * 0.4, 320 * u), h: 66 * u, cor: P.fg, parent: mapa, entrada: 'pop', t0: ti + 0.4, quique: 0.5, nome: 'rotulo' });
        texto(comp, it[i], { fonte: P.fonteTexto, tamanho: 34 * u, cor: P.bg, corDestaque: P.bg, x: px, y: ly, parent: mapa, entrada: 'sobe', t0: ti + 0.46, largura: Math.min(W * 0.36, 290 * u), maxLinhas: 1, sublinhado: false, livre: true, fala: false });
    }
    if (d.titulo) kT(g, comp, P, d.titulo, v ? H * 0.13 : H * 0.14, { maxLinhas: 2, tam: v ? 84 : 72, sobrepoe: true });
    curva(kS(mapa), [[0, [118, 118], null, 25], [comp.duration, [100, 100], 40, null]]);
    cam.empurra(0.8, 0, 0, 1.0).empurra(comp.duration - 0.2, 0, 0, 1.08);
    fecha(comp);
}

// ---------------------------------------------------------------- 8. cards que pulam
function cenaCardsPulam(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12, i;
    var it = kItens(d, ['Um', 'Dois', 'Três'], 4), n = it.length;
    if (d.titulo) kT(g, comp, P, d.titulo, v ? H * 0.15 : H * 0.5, { x: v ? g.cx : W * 0.26, largura: v ? W * 0.86 : W * 0.4, maxLinhas: 3, tam: v ? 90 : 80 });
    var cw = v ? W * 0.8 : W * 0.44, ch = Math.min(v ? H * 0.15 : H * 0.2, (H * 0.62) / n - 16 * u), top = v ? H * 0.31 : H * 0.5 - (n * (ch + 18 * u)) / 2 + ch / 2, cx0 = v ? g.cx : W * 0.72;
    for (i = 0; i < n; i++) {
        var y = top + i * (ch + 18 * u) + (v ? ch / 2 : 0), ti = Q(it[i], T0 + 0.3 + i * 0.42), ult = i == n - 1, rot = (i % 2 ? 1 : -1) * 5;
        var c = forma(comp, 'retangulo', { x: cx0, y: y, w: cw, h: ch, r: 36 * u, cor: ult ? kCardAc(P) : P.card, traco: ult ? null : P.mudo, tracoL: 3 * u, parent: cam.no, nome: 'card' });
        mola(kP(c), ti, [cx0 + (i % 2 ? 1 : -1) * 40 * u, y + H * 0.45], [cx0, y], { quique: 0.75, dur: 0.85 });
        mola(kR(c), ti, rot * 2, 0, { quique: 0.6, dur: 0.9 });
        curva(kO(c), [[ti - 0.04, 0, null, 30], [ti, 100, null, 30]]);
        som(ti + 0.22, 'pop', 0.6);
        var ex = cx0 - cw / 2 + ch * 0.62;
        var ic = elemento(comp, i, { x: ex, y: y, w: ch * 0.78, parent: cam.no, entrada: 'pop', t0: ti + 0.3, quique: 0.7 });
        texto(comp, it[i], { tamanho: Math.min(58 * u, ch * 0.42), cor: ult ? kSobreCard(P) : P.fg, corDestaque: ult ? kSobreCard(P) : P.ac, x: cx0 + ch * 0.4, y: y, parent: cam.no, entrada: 'sobe', t0: ti + 0.3, largura: cw - ch * 1.6, maxLinhas: 2, sublinhado: false, fala: false });
    }
    cam.empurra(0.8, 0, -10 * u, 1.03).empurra(comp.duration - 0.2, 0, 20 * u, 1.08);
    fecha(comp);
}

// ---------------------------------------------------------------- 9. cursor que conduz
function kCursor(comp, parent, u, cor, borda) {
    var L = comp.layers.addShape(); L.name = 'cursor'; if (parent) kPai(L, parent);
    var gp = L.property('ADBE Root Vectors Group').addProperty('ADBE Vector Group'), v = gp.property('ADBE Vectors Group');
    var pa = v.addProperty('ADBE Vector Shape - Group'), S = new Shape(), s = 1.5 * u;
    S.vertices = [[0, 0], [0, 38 * s], [9 * s, 29 * s], [16 * s, 44 * s], [23 * s, 41 * s], [16 * s, 27 * s], [29 * s, 27 * s]];
    var z = [], i; for (i = 0; i < 7; i++) z.push([0, 0]);
    S.inTangents = z; S.outTangents = z; S.closed = true;
    pa.property('ADBE Vector Shape').setValue(S);
    var st = v.addProperty('ADBE Vector Graphic - Stroke'); st.property('ADBE Vector Stroke Color').setValue(kCor(borda)); st.property('ADBE Vector Stroke Width').setValue(4 * u);
    try { st.property('ADBE Vector Stroke Line Join').setValue(2); } catch (e) { }
    v.addProperty('ADBE Vector Graphic - Fill').property('ADBE Vector Fill Color').setValue(kCor(cor));
    kO(L).setValue(0);
    return L;
}
function cenaCursor(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12;
    if (d.titulo) kT(g, comp, P, d.titulo, v ? H * 0.15 : H * 0.15, { maxLinhas: 2, tam: v ? 86 : 70 });
    var pw = v ? W * 0.86 : W * 0.6, ph = v ? H * 0.42 : H * 0.62, px = g.cx, py = v ? H * 0.52 : H * 0.57;
    var painel = forma(comp, 'retangulo', { x: px, y: py, w: pw, h: ph, r: 44 * u, cor: P.card, traco: P.mudo, tracoL: 3 * u, parent: cam.no, entrada: 'cresce', t0: T0, nome: 'painel' });
    var campoW = pw * 0.84, campoH = 110 * u, cy1 = py - ph * 0.2;
    var campo = forma(comp, 'pilula', { x: px, y: cy1, w: campoW, h: campoH, cor: P.bg, traco: P.mudo, tracoL: 3 * u, parent: cam.no, entrada: 'pop', t0: T0 + 0.2, nome: 'campo' });
    var foco = forma(comp, 'pilula', { x: px, y: cy1, w: campoW + 14 * u, h: campoH + 14 * u, traco: P.ac, tracoL: 6 * u, alfa: 0, parent: cam.no, nome: 'foco' });
    var bw = pw * 0.5, bh = 112 * u, by = py + ph * 0.24;
    var bt = botao(comp, { x: px, y: by, w: bw, h: bh, rotulo: d.botao || 'Enviar', cor: P.ac, corTexto: kSobre(P), t0: T0 + 0.4, tam: 46 * u, parent: cam.no });
    var cur = kCursor(comp, cam.no, u, P.fg, P.bg);
    var tc = T0 + 0.9, txt = d.texto || d.sub || d.rotulo || 'o que você quer';
    // o cursor é elemento: clica no fim do campo (o texto digitado nasce à esquerda) e na borda do botão, nunca em cima de letra
    var xt0 = px - campoW / 2 + 36 * u, wt = Math.min(kMedir(comp, txt, P.fonteTexto, 42 * u), campoW * 0.86);
    var pc = kCursorNoBotao(comp, [px - campoW / 2, cy1 - campoH / 2, px + campoW / 2, cy1 + campoH / 2], { x: xt0 + wt / 2, y: cy1, w: wt + 12 * u, h: 60 * u }, 0, 0, 1, [px + campoW * 0.34, cy1 + 6 * u], u);
    var pb = kCursorNoBotao(comp, [px - bt.w / 2, by - bt.h / 2, px + bt.w / 2, by + bt.h / 2], bt.texto, 0, 0, 1, [px + bt.w / 2 - bt.h * 0.45, by + bt.h * 0.12], u);
    kP(cur).setValue([px + pw * 0.6, py + ph * 0.9]);
    curva(kP(cur), [[tc - 0.7, [px + pw * 0.6, py + ph * 0.9], null, 20], [tc, pc, 75, 30]]);
    curva(kO(cur), [[tc - 0.7, 0, null, 30], [tc - 0.5, 100, 60, null]]);
    curva(kS(cur), [[tc, [100, 100], null, 25], [tc + 0.07, [82, 82], 70, 40], [tc + 0.2, [100, 100], 70, null]]);
    curva(kO(foco), [[tc, 0, null, 30], [tc + 0.12, 100, 60, null]]);
    som(tc, 'clique', 0.8);
    var dg = digita(comp, txt, { x: px - campoW / 2 + 36 * u, y: cy1, tamanho: 42 * u, fonte: P.fonteTexto, cor: P.fg, largura: campoW * 0.86, parent: cam.no, t0: tc + 0.25, cps: 26 });
    var t2 = Math.max(dg.fim + 0.15, tc + 0.9);
    curva(kP(cur), [[tc + 0.1, pc, null, 25], [t2, pb, 75, 30]]);
    curva(kS(cur), [[t2, [100, 100], null, 25], [t2 + 0.07, [82, 82], 70, 40], [t2 + 0.2, [100, 100], 70, null]]);
    bt.aperta(t2);
    var res = forma(comp, 'pilula', { x: px, y: py + ph * 0.02, w: campoW * 0.9, h: 96 * u, cor: kCardAc(P), parent: cam.no, entrada: 'pop', t0: t2 + 0.25, quique: 0.7, nome: 'resultado' });
    check(comp, { x: px - campoW * 0.36, y: py + ph * 0.02, tam: 44 * u, cor: kSobreCard(P), parent: cam.no, t0: t2 + 0.4 });
    texto(comp, d.rotulo || 'Pronto!', { fonte: P.fonteTitulo, tamanho: 44 * u, cor: kSobreCard(P), corDestaque: kSobreCard(P), x: px + 20 * u, y: py + ph * 0.02, parent: cam.no, entrada: 'sobe', t0: t2 + 0.3, largura: campoW * 0.6, maxLinhas: 1, sublinhado: false, livre: true, fala: false });
    som(t2 + 0.28, 'brilho', 0.7);
    curva(kO(cur), [[t2 + 0.7, 100, null, 30], [t2 + 0.95, 0, 60, null]]);
    cam.empurra(0.8, 0, 0, 1.02).empurra(tc, 0, -10 * u, 1.06).empurra(t2 + 0.2, 0, 30 * u, 1.1).empurra(comp.duration - 0.2, 0, 40 * u, 1.12);
    fecha(comp);
}

// ---------------------------------------------------------------- 10. notificações empilhadas
function cenaNotificacoes(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12, i;
    var esc = v ? 0.66 * H / 960 * 0.92 : 0.7 * H / 960;
    if (d.titulo) kT(g, comp, P, d.titulo, v ? H * 0.14 : g.cy, { x: v ? g.cx : W * 0.27, largura: v ? W * 0.86 : W * 0.4, maxLinhas: 3, tam: v ? 90 : 80 });
    var cel = celular(comp, { x: v ? g.cx : W * 0.7, y: v ? H * 0.58 : g.cy, escala: esc, parent: cam.no, t0: T0 + 0.1, tela: P.bg == P.card ? '#222222' : (kLum(P.bg) > 0.5 ? '#EDEDF0' : '#1B1B20') });
    var claro = kLum(P.bg) > 0.5, ban = claro ? '#FFFFFF' : '#2E2E35', tx = claro ? '#1B1B1F' : '#F4F4F6';
    var it = kItens(d, ['Nova mensagem', 'Agenda confirmada', 'Pagamento recebido'], 4), n = it.length;
    var hora = texto(comp, d.rotulo || '09:41', { fonte: P.fonteTitulo, tamanho: 120, cor: tx, corDestaque: tx, x: 0, y: -290, parent: cel.no, entrada: 'sobe', t0: T0 + 0.7, largura: 340, maxLinhas: 1, sublinhado: false, livre: true, fala: false });
    for (i = 0; i < n; i++) {
        var ti = Q(it[i], T0 + 0.6 + i * 0.3), y = -130 + i * 128;
        var b = forma(comp, 'retangulo', { x: 0, y: y, w: 392, h: 110, r: 30, cor: ban, parent: cel.no, nome: 'banner' });
        curva(kP(b), [[ti, [0, y - 520], null, 22], [ti + 0.45, [0, y], 80, null]]);
        mola(kS(b), ti + 0.3, [96, 96], [100, 100], { quique: 0.5, dur: 0.4 });
        curva(kO(b), [[ti - 0.04, 0, null, 30], [ti, 100, null, 30]]);
        var ic = forma(comp, 'retangulo', { x: -152, y: y, w: 62, h: 62, r: 16, cor: i % 2 ? P.ac : P.fg, parent: cel.no, nome: 'icone-app' });
        curva(kP(ic), [[ti, [-152, y - 520], null, 22], [ti + 0.45, [-152, y], 80, null]]);
        curva(kO(ic), [[ti - 0.04, 0, null, 30], [ti, 100, null, 30]]);
        var tt = texto(comp, it[i], { tamanho: 30, cor: tx, x: 38, y: y, parent: cel.no, entrada: 'sobe', t0: ti + 0.4, largura: 250, maxLinhas: 2, sublinhado: false, livre: true, fala: false });
        som(ti + 0.1, 'notif', 0.8);
    }
    var bd = forma(comp, 'circulo', { x: 150, y: -390, w: 70, cor: '#FF3B30', parent: cel.no, entrada: 'pop', t0: T0 + 1.0, quique: 0.8, nome: 'badge' });
    var bn = comp.layers.addText('0'); bn.name = 'badge-num'; kPai(bn, cel.no);
    var td = kDoc(bn, P.fonteTitulo, 40, '#FFFFFF'); kP(bn).setValue([150, -377]);
    td.expression = 'var n = Math.floor((time - ' + (T0 + 1.0) + ') / 0.6) + 1; String(Math.max(0, Math.min(' + n + ', n)))';
    cam.empurra(0.8, 0, 0, 1.02).empurra(comp.duration - 0.2, v ? 0 : 40 * u, v ? 40 * u : 0, 1.1);
    fecha(comp);
}

// ---------------------------------------------------------------- 11. chat com balões
function cenaChat(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12, i;
    var it = kItens(d, ['Oi, tudo bem?', 'Vi que você sumiu', 'Posso ajudar?'], 4), n = it.length;
    if (d.titulo) kT(g, comp, P, d.titulo, v ? H * 0.12 : H * 0.14, { maxLinhas: 2, tam: v ? 80 : 66 });
    var cw = v ? W * 0.84 : W * 0.5, top = v ? H * 0.3 : H * 0.28, passo = Math.min(v ? H * 0.14 : H * 0.17, (H * 0.5) / n), y = top + passo * 0.5;
    var tam = Math.max(56 * u, H * 0.036), maxw = cw * 0.78, fonte = P.fonteTexto;
    for (i = 0; i < n; i++) {
        var meu = i % 2 == 1, ti = Q(it[i], T0 + 0.3 + i * 0.4);
        var cor = meu ? P.ac : P.card, tcor = meu ? kSobre(P) : P.fg;
        // o balão é medido ANTES (largura do texto, linhas) e desenhado por baixo do texto, com cauda, encostado no lado de quem fala
        var w0 = kMedir(comp, it[i], fonte, tam), linhas = Math.max(1, Math.ceil(w0 / maxw)), tw = Math.min(w0 + 8, maxw);
        var bwid = tw + 64 * u, bhei = linhas * tam * 1.3 + 44 * u;
        var cxb = meu ? g.cx + cw / 2 - bwid / 2 : g.cx - cw / 2 + bwid / 2;
        var bx = meu ? g.cx + cw / 2 : g.cx - cw / 2, k;
        for (k = 0; k < 3; k++) {   // três pontinhos "digitando" antes do balão
            var pt = forma(comp, 'circulo', { x: bx + (meu ? -1 : 1) * (50 + k * 30) * u, y: y, w: 18 * u, cor: P.mudo, parent: cam.no, nome: 'digitando', alfa: 0 });
            curva(kO(pt), [[ti - 0.3, 0, null, 30], [ti - 0.22, 100, 60, 30], [ti - 0.04, 100, null, 30], [ti, 0, null, 30]]);
            kP(pt).expression = 'value + [0, Math.sin(time*9+' + k + ')*5]';
        }
        var b = forma(comp, 'retangulo', { x: cxb, y: y, w: bwid, h: bhei, r: 44 * u, cor: cor, parent: cam.no, nome: 'balao' });
        var sg = meu ? 1 : -1, ex = meu ? cxb + bwid / 2 : cxb - bwid / 2, ey = y + bhei / 2;
        var cauda = caminho(comp, [[ex - sg * 46 * u, ey - 24 * u], [ex + sg * 14 * u, ey + 22 * u], [ex - sg * 4 * u, ey - 48 * u]], { preenche: cor, fecha: true, parent: cam.no, nome: 'balao-cauda' });
        var tt = texto(comp, it[i], { fonte: fonte, tamanho: tam, cor: tcor, corDestaque: tcor, x: cxb, y: y, parent: cam.no, entrada: 'sobe', t0: ti + 0.04, largura: tw + 8, maxLinhas: linhas, sublinhado: false, fala: false, sobrepoe: true, livre: true });
        mola(kS(b), ti, [60, 60], [100, 100], { quique: 0.7, dur: 0.55 });
        curva(kO(b), [[ti - 0.03, 0, null, 30], [ti, 100, null, 30]]);
        curva(kO(cauda), [[ti - 0.03, 0, null, 30], [ti, 100, null, 30]]);
        mola(kS(cauda), ti, [60, 60], [100, 100], { quique: 0.7, dur: 0.55 });
        som(ti, meu ? 'tap' : 'notif', 0.6);
        y += passo;
    }
    cam.empurra(0.8, 0, 0, 1.02).empurra(comp.duration - 0.2, 0, -20 * u, 1.07);
    fecha(comp);
}

// ---------------------------------------------------------------- 12. linha do tempo
function cenaLinhaTempo(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12, i;
    var it = kItens(d, ['Dia 1', 'Semana 2', 'Mês 3'], 5), n = it.length;
    if (d.titulo) kT(g, comp, P, d.titulo, v ? H * 0.14 : H * 0.16, { maxLinhas: 2, tam: v ? 88 : 72 });
    var a = v ? [W * 0.28, H * 0.3] : [W * 0.14, H * 0.58], b = v ? [W * 0.28, H * 0.86] : [W * 0.86, H * 0.58];
    caminho(comp, [a, b], { cor: P.mudo, esp: 10 * u, t0: T0, dur: 0.6, parent: cam.no, nome: 'trilho' });
    var prog = caminho(comp, [a, b], { cor: P.ac, esp: 14 * u, t0: T0 + 0.4, dur: 1.1, parent: cam.no, nome: 'progresso' });
    var cometa = forma(comp, 'circulo', { x: a[0], y: a[1], w: 44 * u, cor: P.ac, parent: cam.no, nome: 'cometa', alfa: 0 });
    var tl = T0 + 0.4, dd = 1.1;
    curva(kP(cometa), [[tl, a, null, 18], [tl + dd, b, 78, null]]); curva(kO(cometa), [[tl, 0, null, 30], [tl + 0.1, 100, 60, 30]]);
    for (i = 0; i < n; i++) {
        var f = n == 1 ? 0.5 : i / (n - 1), px = a[0] + (b[0] - a[0]) * f, py = a[1] + (b[1] - a[1]) * f, ti = Q(it[i], tl + dd * f);
        var no = forma(comp, 'circulo', { x: px, y: py, w: 64 * u, cor: i == n - 1 ? P.ac : P.bg, traco: P.ac, tracoL: 10 * u, parent: cam.no, entrada: 'pop', t0: ti, quique: 0.7, nome: 'marco' });
        som(ti, 'ponto' + (1 + i % 5), 0.6);
        var tx = v ? px + 70 * u : px, ty = v ? py : py - 110 * u - (i % 2) * 0;
        if (!v && i % 2) ty = py + 120 * u;
        texto(comp, it[i], { tamanho: (v ? 52 : 44) * u, cor: P.fg, corDestaque: P.ac, x: v ? px + 60 * u + (W * 0.4) / 2 : px, y: v ? py : ty, parent: cam.no, entrada: v ? 'mascara' : 'sobe', t0: ti + 0.1, largura: v ? W * 0.4 : W * 0.2, maxLinhas: 2, sublinhado: false, fala: false });
        elemento(comp, i, { x: v ? W * 0.8 : px, y: v ? py : (i % 2 ? py + 50 * u : py - 50 * u) + (v ? 0 : 0), w: v ? 130 * u : 0.01, parent: cam.no, entrada: v ? 'pop' : null, t0: ti + 0.2 });
    }
    cam.empurra(0.8, 0, 0, 1.02).empurra(comp.duration - 0.2, 0, 0, 1.07);
    fecha(comp);
}

// ---------------------------------------------------------------- 13. laço de processo
function cenaLaco(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12, i;
    var it = kItens(d, ['Ideia', 'Teste', 'Ajuste'], 5), n = it.length;
    var cx = g.cx, cy = v ? H * 0.5 : H * 0.54, R = Math.min(W, H) * (v ? 0.24 : 0.3);
    if (d.titulo) kT(g, comp, P, d.titulo, v ? H * 0.17 : H * 0.14, { maxLinhas: 2, tam: v ? 76 : 66 });
    var trilho = forma(comp, 'circulo', { x: cx, y: cy, w: R * 2, traco: P.mudo, tracoL: 8 * u, alfa: 60, parent: cam.no, entrada: 'cresce', t0: T0, nome: 'trilho' });
    var prog = forma(comp, 'circulo', { x: cx, y: cy, w: R * 2, traco: P.ac, tracoL: 14 * u, parent: cam.no, nome: 'progresso' });
    kR(prog).setValue(-90);
    var gg = prog.property('ADBE Root Vectors Group').property(1).property('ADBE Vectors Group');
    var tm = gg.addProperty('ADBE Vector Filter - Trim');
    var dd = 1.2;
    curva(tm.property('ADBE Vector Trim End'), [[T0 + 0.4, 0, null, 20], [T0 + 0.4 + dd, 100, 80, null]]);
    try { gg.property('ADBE Vector Graphic - Stroke').property('ADBE Vector Stroke Line Cap').setValue(2); } catch (e) { }
    // o ponto que orbita o laço
    var orb = grupo(comp, { x: cx, y: cy, parent: cam.no, nome: 'orbita' });
    curva(kR(orb), [[T0 + 0.4, -90, null, 20], [T0 + 0.4 + dd, 270, 80, null]]);
    forma(comp, 'circulo', { x: R, y: 0, w: 40 * u, cor: P.fg, parent: orb, nome: 'satelite' });
    for (i = 0; i < n; i++) {
        var a = -Math.PI / 2 + i * 2 * Math.PI / n, px = cx + Math.cos(a) * R, py = cy + Math.sin(a) * R, ti = Q(it[i], T0 + 0.4 + dd * i / n);
        forma(comp, 'circulo', { x: px, y: py, w: 132 * u, cor: P.bg, traco: P.ac, tracoL: 9 * u, parent: cam.no, entrada: 'pop', t0: ti, quique: 0.7, nome: 'etapa' });
        elemento(comp, i, { x: px, y: py, w: 84 * u, parent: cam.no, entrada: 'pop', t0: ti + 0.06, quique: 0.7 });
        var lx = px, ly = py + (Math.sin(a) < -0.5 ? -118 * u : 118 * u);
        texto(comp, it[i], { opcional: true, fonte: P.fonteTexto, tamanho: 40 * u, cor: P.fg, corDestaque: P.ac, x: lx, y: ly, parent: cam.no, entrada: 'sobe', t0: ti + 0.1, largura: W * 0.26, maxLinhas: 2, sublinhado: false, fala: false });
        som(ti, 'ponto' + (1 + i % 5), 0.6);
    }
    som(T0 + 0.4, 'sobe', 0.4);
    if (d.rotulo) texto(comp, d.rotulo, { fonte: P.fonteTitulo, tamanho: 52 * u, cor: P.fg, corDestaque: P.ac, x: cx, y: cy, parent: cam.no, entrada: 'palavra', t0: Q(d.rotulo, T0 + 0.6), largura: R * 1.1, maxLinhas: 3, sublinhado: false, livre: true, fala: false });
    cam.empurra(0.8, 0, 0, 1.0).empurra(comp.duration - 0.2, 0, 0, 1.08);
    fecha(comp);
}

// ---------------------------------------------------------------- 14. explosão de elementos reais
function cenaExplosao(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12, i;
    var it = kItens(d, ['Um', 'Dois', 'Três', 'Quatro'], 4), n = Math.max(3, Math.min(4, it.length));
    var cx = g.cx, cy = v ? H * 0.46 : g.cy, R = Math.min(W, H) * (v ? 0.34 : 0.34);
    var hub = forma(comp, 'circulo', { x: cx, y: cy, w: Math.min(W, H) * 0.26, cor: P.ac, parent: cam.no, entrada: 'pop', t0: T0, quique: 0.6, nome: 'centro' });
    som(T0, 'impacto', 0.6);
    if (d.titulo) texto(comp, d.titulo, { fonte: P.fonteTitulo, tamanho: 54 * u, cor: kSobre(P), corDestaque: kSobre(P), x: cx, y: cy, parent: cam.no, entrada: 'palavra', t0: Q(d.titulo, T0 + 0.1), largura: Math.min(W, H) * 0.22, maxLinhas: 3, sublinhado: false, livre: true, fala: false });
    for (i = 0; i < n; i++) {
        var a = -Math.PI / 2 + i * 2 * Math.PI / n + 0.3, ew = Math.min(W, H) * 0.2, x = Math.max(W * KIT.MX + ew / 2, Math.min(W * (1 - KIT.MX) - ew / 2, cx + Math.cos(a) * R * (v ? 1.0 : 1.5))), y = Math.max(H * 0.17 + ew / 2, Math.min(H * 0.74 - ew, cy + Math.sin(a) * R * (v ? 1.22 : 1.0))), ti = Q(it[i] || '', T0 + 0.45 + i * 0.22);
        var e = elemento(comp, i, { x: cx, y: cy, w: Math.min(W, H) * 0.2, parent: cam.no, nome: 'el-explode' });
        curva(kO(e), [[ti - 0.04, 0, null, 30], [ti, 100, null, 30]]);
        mola(kP(e), ti, [cx, cy], [x, y], { quique: 0.7, dur: 0.85 });
        mola(kS(e), ti, [30, 30], [100 * 1.0, 100 * 1.0], { quique: 0.6, dur: 0.8 });
        mola(kR(e), ti, (i % 2 ? 1 : -1) * 120, 0, { quique: 0.5, dur: 0.9 });
        som(ti, 'pop', 0.7);
        if (it[i]) texto(comp, it[i], { opcional: true, fonte: P.fonteTexto, tamanho: 38 * u, cor: P.fg, corDestaque: P.ac, x: x, y: y + Math.min(W, H) * 0.14, parent: cam.no, entrada: 'sobe', t0: ti + 0.35, largura: W * 0.32, maxLinhas: 2, sublinhado: false, fala: false });
        kEstouro(comp, cam, x, y, ti + 0.05, i % 2 ? P.ac : P.fg, 5, 90 * u, false);
        kP(e).expression = 'value + [Math.sin(time*1.8+' + i + ')*8, Math.cos(time*1.5+' + i + ')*8]';
    }
    cam.empurra(0.8, 0, 0, 1.0).empurra(comp.duration - 0.2, 0, -10 * u, 1.09);
    fecha(comp);
}

// ---------------------------------------------------------------- 15. máscara que revela
function cenaRevela(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12;
    var cx = g.cx, cy = v ? H * 0.4 : g.cy, D = Math.min(W, H) * (v ? 0.84 : 0.8);
    var disco = comp.layers.addSolid(kCor(kContr(P.ac, P.bg) > 1.8 ? P.ac : P.fg), 'revela-disco', W, H, 1); kPai(disco, cam.no);
    kMascaraAnim(disco, kCircShape(cx, cy, 2), kCircShape(cx, cy, D / 2), T0, 0.9);
    var el = elemento(comp, 0, { x: cx, y: cy, w: D * 0.82, parent: cam.no, flutua: 10 });
    curva(kO(el), [[T0 + 0.35, 0, null, 30], [T0 + 0.6, 100, 60, null]]);
    mola(kS(el), T0 + 0.35, [40, 40], [100, 100], { quique: 0.6, dur: 0.8 });
    // a palavra gigante atrás, revelada em faixas
    var gig = d.rotulo || d.sub || '';
    if (gig) {
        var gt = texto(comp, String(gig).toUpperCase(), { fonte: P.fonteTitulo, tamanho: 260 * u, cor: P.card, corDestaque: P.card, x: cx, y: cy, parent: cam.no, entrada: 'mascara', t0: T0 + 0.1, largura: W * 0.9, maxLinhas: 1, sublinhado: false, livre: true, fala: false, caixa: false, semAuditoria: true });
        var j; for (j = 0; j < gt.camadas.length; j++) gt.camadas[j].moveToEnd();
    }
    disco.moveToEnd();
    som(T0, 'sobe', 0.6); som(T0 + 0.9, 'impacto', 0.7);
    kEstouro(comp, cam, cx, cy, T0 + 0.9, P.fg, 10, D * 0.6, false);
    if (d.titulo) kT(g, comp, P, d.titulo, v ? H * 0.78 : g.cy, { x: v ? g.cx : W * 0.2, largura: v ? W * 0.86 : W * 0.34, maxLinhas: 3, tam: v ? 92 : 80, t0: T0 + 1.0 });
    cam.empurra(0.8, 0, 0, 1.0).empurra(comp.duration - 0.2, 0, 0, 1.1);
    fecha(comp);
}

// ---------------------------------------------------------------- 16. split antes/depois com divisor que varre
function cenaSplit(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, v = g.v, T0 = 0.12;
    // split LIMPO com a paleta do vídeo: dois painéis de tela cheia, sem pai (não andam com a câmera: nenhuma borda aparece), antes à esquerda/
    // em cima e depois (cor de destaque) varrido por uma máscara; os textos ficam por cima do painel certo
    var pb = KIT.paletaBase || { fundo: P.bg, tinta: P.fg, destaque: P.ac };
    var corAntes = pb.fundo, tAntes = pb.tinta, corDepois = pb.destaque, tDepois = kLum(pb.destaque) > 0.5 ? '#14110F' : '#FFFFFF';
    var base = comp.layers.addSolid(kCor(corAntes), 'antes-fundo', W, H, 1);
    var ts = T0 + 1.2, dd = 0.7;
    var al = v ? [g.cx, H * 0.34] : [W * 0.25, g.cy], bl = v ? [g.cx, H * 0.7] : [W * 0.75, g.cy], wd = v ? W * 0.8 : W * 0.38, off = v ? 150 : 230;
    texto(comp, 'ANTES', { fonte: P.fonteTitulo, tamanho: 36 * u, cor: tAntes, corDestaque: tAntes, x: al[0], y: al[1] - off * u, entrada: 'sobe', t0: T0 + 0.1, largura: wd, maxLinhas: 1, sublinhado: false, fala: false, caixa: false, sobrepoe: true });
    texto(comp, d.antes || '', { tamanho: 92 * u, cor: tAntes, corDestaque: tAntes, x: al[0], y: al[1], entrada: 'palavra', t0: Q(d.antes || '', T0 + 0.3), largura: wd, maxLinhas: 3, sublinhado: false, sobrepoe: true });
    elemento(comp, 0, { x: al[0], y: al[1] + (v ? 240 : 250) * u, w: 150 * u, entrada: 'pop', t0: T0 + 0.2 });
    var depois = comp.layers.addSolid(kCor(corDepois), 'depois-fundo', W, H, 1);   // por cima do antes: o que vem depois cobre o que ficou para trás
    var m0 = v ? kRetShape(0, 0, W, 0, 0) : kRetShape(0, 0, 0, H, 0), m1 = kRetShape(0, 0, W, H, 0);
    var mk = depois.property('ADBE Mask Parade').addProperty('ADBE Mask Atom'), mp = mk.property('ADBE Mask Shape');
    mp.setValue(m0); mp.setValueAtTime(kFrame(ts), m0); mp.setValueAtTime(kFrame(ts) + dd, m1);
    var div = forma(comp, 'retangulo', { x: g.cx, y: g.cy, w: v ? W : 12 * u, h: v ? 12 * u : H, cor: tDepois, nome: 'divisor', alfa: 0 });
    curva(kP(div), [[ts, v ? [g.cx, 0] : [0, g.cy], null, 25], [ts + dd, v ? [g.cx, H] : [W, g.cy], 75, null]]);
    curva(kO(div), [[ts - 0.02, 0, null, 30], [ts, 100, null, 30], [ts + dd, 100, null, 30], [ts + dd + 0.1, 0, 60, null]]);
    texto(comp, 'DEPOIS', { fonte: P.fonteTitulo, tamanho: 36 * u, cor: tDepois, corDestaque: tDepois, x: bl[0], y: bl[1] - off * u, entrada: 'sobe', t0: ts + dd, largura: wd, maxLinhas: 1, sublinhado: false, fala: false, caixa: false, sobrepoe: true });
    texto(comp, d.depois || '', { fonte: P.fonteTitulo, tamanho: 104 * u, cor: tDepois, corDestaque: tDepois, x: bl[0], y: bl[1], entrada: 'palavra', t0: Math.max(ts + dd - 0.2, Q(d.depois || '', ts + dd)), largura: wd, maxLinhas: 3, sublinhado: false, sobrepoe: true });
    elemento(comp, 1, { x: bl[0], y: bl[1] + (v ? 240 : 250) * u, w: 170 * u, entrada: 'pop', t0: ts + dd + 0.2 });
    som(T0 + 0.3, 'pop', 0.5); som(ts, 'troca', 0.8);
    KIT.fit = false;
    fecha(comp);
    KIT.fit = true;
}

// ---------------------------------------------------------------- 17. objeto herói 3D falso, com sombra e anéis
function cenaHeroi(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12, i;
    var cx = v ? g.cx : W * 0.34, cy = v ? H * 0.38 : g.cy, S = Math.min(W, H) * (v ? 0.56 : 0.62);
    var halo = forma(comp, 'circulo', { x: cx, y: cy, w: S * 1.25, cor: P.ac, alfa: 22, parent: cam.no, entrada: 'cresce', t0: T0, nome: 'halo' });
    for (i = 0; i < 2; i++) {
        var an = forma(comp, 'circulo', { x: cx, y: cy, w: S * (1.0 + i * 0.28), traco: P.ac, tracoL: 5 * u, alfa: 55 - i * 20, parent: cam.no, entrada: 'cresce', t0: T0 + 0.1 + i * 0.1, nome: 'anel' });
        kS(an).expression = 'var s = [value[0], value[1] * 0.34]; s';
        kR(an).expression = 'time * ' + (i ? -24 : 30);
    }
    sombraChao(comp, cx, cy + S * 0.5, S * 0.55, { parent: cam.no, t0: T0 + 0.2, alfa: 30 });
    var h = elemento(comp, 0, { x: cx, y: cy, w: S * 0.72, parent: cam.no, entrada: 'pop', t0: T0 + 0.1, quique: 0.7 });
    try {   // 3D de verdade: o objeto balança em Y e X com perspectiva
        h.threeDLayer = true;
        h.property('ADBE Transform Group').property('ADBE Rotate Y').expression = 'Math.sin(time * 1.6) * 38';
        h.property('ADBE Transform Group').property('ADBE Rotate X').expression = 'Math.cos(time * 1.2) * 12';
        kP(h).expression = 'value + [0, Math.sin(time * 2) * 14, 0]';
    } catch (e) { kP(h).expression = 'value + [0, Math.sin(time*2)*14]'; }
    kR(h).expression = 'Math.sin(time*1.4)*6';
    som(T0 + 0.1, 'impacto', 0.7);
    var it = kItens(d, [], 3), n = it.length;
    var tx = v ? g.cx : W * 0.7;
    if (d.titulo) kT(g, comp, P, d.titulo, v ? H * 0.12 : H * 0.28, { x: v ? g.cx : W * 0.7, largura: v ? W * 0.86 : W * 0.44, maxLinhas: 3, tam: v ? 88 : 76, t0: T0 + 0.4, sobrepoe: true });
    for (i = 0; i < n; i++) {
        var ti = Q(it[i], T0 + 0.9 + i * 0.4), y = v ? H * 0.64 + i * H * 0.078 : H * 0.5 + i * H * 0.1, lw = v ? W * 0.7 : W * 0.34, x = v ? g.cx : W * 0.7;
        forma(comp, 'pilula', { x: x, y: y, w: lw, h: (v ? 78 : 72) * u, cor: P.card, traco: P.ac, tracoL: 4 * u, parent: cam.no, entrada: 'pop', t0: ti, quique: 0.6, nome: 'callout' });
        forma(comp, 'circulo', { x: x - lw / 2 + 40 * u, y: y, w: 26 * u, cor: P.ac, parent: cam.no, entrada: 'pop', t0: ti + 0.05, nome: 'bolinha' });
        texto(comp, it[i], { fonte: P.fonteTexto, tamanho: 36 * u, cor: P.fg, corDestaque: P.ac, x: x + 22 * u, y: y, parent: cam.no, entrada: 'sobe', t0: ti + 0.08, largura: lw - 110 * u, maxLinhas: 1, sublinhado: false, fala: false });
        som(ti, 'pop', 0.55);
    }
    particulas(comp, { n: 14, cor: P.ac, parent: cam.no, tipo: 'flutua', raio: 9, area: [cx - S * 0.6, cy - S * 0.6, cx + S * 0.6, cy + S * 0.5], alfa: 80, t0: T0 + 0.3 });
    cam.empurra(0.8, 0, 0, 1.0).empurra(comp.duration - 0.2, 0, 0, 1.1);
    fecha(comp);
}

// ---------------------------------------------------------------- 18. brasas/partículas em volta de uma frase
function cenaParticulas(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.14;
    particulas(comp, { n: 46, cores: [P.ac, P.fg, P.ac, P.mudo], parent: cam.no, tipo: d.queda ? 'cai' : 'sobe', raio: 15, area: [0, H * 0.15, W, H * 1.1], alfa: 85, t0: 0.05, gira: !!d.queda, forma: d.queda ? 'retangulo' : 'circulo' });
    var tt = texto(comp, d.titulo || d.texto || '', { tamanho: (v ? 150 : 124) * u, cor: P.fg, corDestaque: P.ac, x: g.cx, y: g.cy, parent: cam.no, entrada: 'voa', t0: T0, largura: W * (v ? 0.86 : 0.78), maxLinhas: 4 });
    var i; for (i = 0; i < tt.tempos.length; i++) som(tt.tempos[i], 'pop', 0.55);
    som(T0, 'sobe', 0.5); if (tt.tempos.length) som(tt.fim - 0.3, 'brilho', 0.6);
    if (d.sub) texto(comp, d.sub, { fonte: P.fonteTexto, tamanho: 50 * u, cor: P.mudo, x: g.cx, y: tt.y + tt.h / 2 + 90 * u, parent: cam.no, entrada: 'sobe', t0: Q(d.sub, tt.fim), largura: W * 0.74, maxLinhas: 2, sublinhado: false });
    cam.empurra(0.9, 0, -20 * u, 1.04).empurra(comp.duration - 0.3, 0, 30 * u, 1.1);
    fecha(comp);
}

// ---------------------------------------------------------------- 19. relógio
function cenaRelogio(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12, i;
    var cx = v ? g.cx : W * 0.34, cy = v ? H * 0.42 : g.cy, R = Math.min(W, H) * (v ? 0.34 : 0.32);
    var rel = grupo(comp, { x: cx, y: cy, parent: cam.no, nome: 'relogio' });
    forma(comp, 'circulo', { x: 0, y: 0, w: R * 2, cor: P.card, traco: P.fg, tracoL: 14 * u, parent: rel, entrada: 'pop', t0: T0, quique: 0.6, nome: 'mostrador' });
    for (i = 0; i < 12; i++) {
        var a = i * Math.PI / 6, tk = forma(comp, 'retangulo', { x: Math.sin(a) * R * 0.84, y: -Math.cos(a) * R * 0.84, w: (i % 3 == 0 ? 12 : 6) * u, h: (i % 3 == 0 ? 40 : 22) * u, cor: i % 3 == 0 ? P.ac : P.mudo, parent: rel, nome: 'marca' });
        kR(tk).setValue(i * 30); curva(kO(tk), [[T0 + 0.2 + i * 0.03, 0, null, 30], [T0 + 0.3 + i * 0.03, 100, 60, null]]);
    }
    var hm = (String(d.valor || '7:00').match(/(\d+)[h:](\d*)/) || [0, 7, 0]), hora = parseInt(hm[1], 10) % 12, minu = parseInt(hm[2] || '0', 10) || 0;
    var hA = forma(comp, 'pilula', { x: 0, y: 0, w: 18 * u, h: R * 0.5, cor: P.fg, parent: rel, nome: 'hora' }); kA(hA).setValue([0, R * 0.25]);
    var mA = forma(comp, 'pilula', { x: 0, y: 0, w: 12 * u, h: R * 0.74, cor: P.ac, parent: rel, nome: 'minuto' }); kA(mA).setValue([0, R * 0.37]);
    var t1 = T0 + 0.5, t2 = t1 + 1.5;
    curva(kR(hA), [[t1, 0, null, 20], [t2, hora * 30 + minu / 2 + 360, 80, null]]);
    curva(kR(mA), [[t1, 0, null, 20], [t2, minu * 6 + 1440, 80, null]]);
    forma(comp, 'circulo', { x: 0, y: 0, w: 34 * u, cor: P.fg, parent: rel, nome: 'eixo' });
    som(t1, 'sobe', 0.5);
    var ts = t2;
    kP(rel).expression = 'var p = value; if (time > ' + ts.toFixed(2) + ' && time < ' + (ts + 0.7).toFixed(2) + ') { p + [Math.sin(time*60)*9, 0] } else p';
    som(ts, 'sino', 0.9);
    var vt = d.valor || '7:00';
    var tx = v ? g.cx : W * 0.72, ty = v ? H * 0.68 : g.cy - H * 0.06;
    texto(comp, vt, { fonte: P.fonteTitulo, tamanho: (v ? 170 : 170) * u, cor: P.ac, corDestaque: P.ac, x: tx, y: ty, parent: cam.no, entrada: 'palavra', t0: Math.max(ts, Q(vt, ts)), largura: W * (v ? 0.8 : 0.4), maxLinhas: 1, sublinhado: false, fala: false });
    if (d.titulo) kT(g, comp, P, d.titulo, v ? H * 0.12 : g.cy + H * 0.1, { x: v ? g.cx : W * 0.72, largura: v ? W * 0.86 : W * 0.4, maxLinhas: 2, tam: v ? 80 : 64, sobrepoe: true });
    kEstouro(comp, cam, cx, cy, ts, P.ac, 10, R * 1.15, false);
    cam.empurra(0.8, 0, 0, 1.0).empurra(ts, 0, -10 * u, 1.08).empurra(comp.duration - 0.2, 0, 0, 1.1);
    fecha(comp);
}

// ---------------------------------------------------------------- 20. calendário que se preenche
function cenaCalendario(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12, i;
    if (d.titulo) kT(g, comp, P, d.titulo, v ? H * 0.15 : g.cy, { x: v ? g.cx : W * 0.24, largura: v ? W * 0.86 : W * 0.36, maxLinhas: 3, tam: v ? 88 : 74 });
    var cols = v ? 5 : 7, rows = v ? 6 : 5, area = v ? [W * 0.1, H * 0.3, W * 0.9, H * 0.72] : [W * 0.46, H * 0.16, W * 0.92, H * 0.84];
    var cw = (area[2] - area[0]) / cols, ch = (area[3] - area[1]) / rows, s = Math.min(cw, ch) * 0.78, k = 0;
    var cheios = {}, seq = [3, 8, 10, 15, 17, 22, 24, 29, 31, 5, 12, 19, 26, 33, 1, 7, 14, 21, 28, 34];
    var nfill = Math.min(seq.length, 14);
    for (i = 0; i < nfill; i++) cheios[seq[i]] = i;
    for (i = 0; i < cols * rows; i++) {
        var c = i % cols, r = Math.floor(i / cols), x = area[0] + cw * (c + 0.5), y = area[1] + ch * (r + 0.5), ti = T0 + (c + r) * 0.03;
        var cel = forma(comp, 'retangulo', { x: x, y: y, w: s, h: s, r: s * 0.26, cor: P.card, traco: P.mudo, tracoL: 2 * u, parent: cam.no, entrada: 'pop', t0: ti, quique: 0.4, nome: 'dia' });
        if (cheios[i] != null) {
            var tf = T0 + 0.6 + cheios[i] * 0.06;
            try { curva(kFillProp(cel), [[tf, kCor(P.card), null, 30], [tf + 0.15, kCor(P.ac), 60, null]]); } catch (e) { }
            curva(kS(cel), [[tf, [100, 100], null, 25], [tf + 0.08, [116, 116], 70, 40], [tf + 0.3, [100, 100], 70, null]]);
            check(comp, { x: x, y: y, tam: s * 0.42, cor: kSobre(P), parent: cam.no, t0: tf + 0.06 });
            if (cheios[i] % 2 == 0) som(tf, 'tique', 0.45);
        }
    }
    som(T0 + 0.8, 'sobe', 0.4);
    if (d.valor) {
        var tv = T0 + 0.6 + nfill * 0.06;
        forma(comp, 'pilula', { x: v ? g.cx : W * 0.24, y: v ? H * 0.75 : H * 0.72, w: 360 * u, h: 110 * u, cor: P.ac, parent: cam.no, entrada: 'pop', t0: Math.min(tv, comp.duration - 0.8), quique: 0.7, nome: 'meta' });
        texto(comp, d.valor, { fonte: P.fonteTitulo, tamanho: 62 * u, cor: kSobre(P), corDestaque: kSobre(P), x: v ? g.cx : W * 0.24, y: v ? H * 0.75 : H * 0.72, parent: cam.no, entrada: 'palavra', t0: Math.min(tv, comp.duration - 0.8) + 0.06, largura: 330 * u, maxLinhas: 1, sublinhado: false, fala: false });
        som(Math.min(tv, comp.duration - 0.8), 'caixa', 0.7);
    }
    cam.empurra(0.8, 0, 0, 1.0).empurra(comp.duration - 0.2, 0, 0, 1.06);
    fecha(comp);
}

// ---------------------------------------------------------------- 21. funil com pontos que caem
function cenaFunil(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12, i;
    var it = kItens(d, ['Contatos', 'Respostas', 'Agendas'], 4), n = it.length;
    if (d.titulo) kT(g, comp, P, d.titulo, v ? H * 0.14 : g.cy, { x: v ? g.cx : W * 0.22, largura: v ? W * 0.86 : W * 0.34, maxLinhas: 2, tam: v ? 86 : 72 });
    var fx = v ? g.cx : W * 0.66, top = v ? H * 0.27 : H * 0.14, bot = v ? H * 0.8 : H * 0.86, fh = (bot - top) / n, wTop = v ? W * 0.84 : W * 0.5, wBot = wTop * 0.3;
    for (i = 0; i < n; i++) {
        var w0 = wTop + (wBot - wTop) * i / n, w1 = wTop + (wBot - wTop) * (i + 1) / n, y0 = top + i * fh, y1 = y0 + fh - 8 * u, ti = Q(it[i], T0 + 0.15 + i * 0.25);
        var tz = caminho(comp, [[fx - w0 / 2, y0], [fx + w0 / 2, y0], [fx + w1 / 2, y1], [fx - w1 / 2, y1]], { preenche: i == n - 1 ? P.ac : P.card, cor: i == n - 1 ? null : P.mudo, esp: 3 * u, fecha: true, parent: cam.no, nome: 'estagio' });
        curva(kO(tz), [[ti, 0, null, 30], [ti + 0.3, 100, 60, null]]);
        mola(kS(tz), ti, [88, 88], [100, 100], { quique: 0.4, dur: 0.5 });
        texto(comp, it[i], { fonte: P.fonteTexto, tamanho: 38 * u, cor: i == n - 1 ? kSobre(P) : P.fg, corDestaque: P.ac, x: fx, y: (y0 + y1) / 2, parent: cam.no, entrada: 'sobe', t0: ti + 0.1, largura: Math.max(180 * u, w1 * 0.9), maxLinhas: 1, sublinhado: false, fala: false });
        som(ti, 'pop', 0.5);
    }
    // pontos que caem pelo funil (alguns somem a cada estágio)
    var np = 18;
    for (i = 0; i < np; i++) {
        var dx = ((i * 37) % 100) / 100 - 0.5, p = forma(comp, 'circulo', { x: fx + dx * wTop * 0.7, y: top - 20 * u, w: 22 * u, cor: i % 5 == 0 ? P.fg : P.ac, parent: cam.no, nome: 'ponto', alfa: 0 });
        var vel = 0.35 + (i % 4) * 0.05, off = i / np, alvoX = fx + dx * wBot * 0.9;
        kP(p).expression = 'var q = ((time - ' + (T0 + 0.8) + ') * ' + vel.toFixed(2) + ' + ' + off.toFixed(2) + ') % 1; var yy = ' + (top - 20 * u).toFixed(1) + ' + q * ' + (bot - top + 40 * u).toFixed(1) + '; var xx = ' + (fx + dx * wTop * 0.7).toFixed(1) + ' + (' + alvoX.toFixed(1) + ' - ' + (fx + dx * wTop * 0.7).toFixed(1) + ') * q; [xx, yy]';
        kO(p).expression = 'var q = ((time - ' + (T0 + 0.8) + ') * ' + vel.toFixed(2) + ' + ' + off.toFixed(2) + ') % 1; time < ' + (T0 + 0.8) + ' ? 0 : (q > ' + (0.95 - (i % 3) * 0.3).toFixed(2) + ' ? 0 : 100)';
    }
    if (d.valor) {
        var tv = comp.duration - 1.0;
        texto(comp, d.valor, { fonte: P.fonteTitulo, tamanho: 110 * u, cor: kSobre(P), corDestaque: kSobre(P), x: fx, y: bot - fh * 0.5 + 60 * u, parent: cam.no, entrada: 'palavra', t0: Math.max(T0 + n * 0.5, Q(d.valor, tv)), largura: wBot * 1.4, maxLinhas: 1, sublinhado: false, fala: false, livre: true });
        som(tv, 'caixa', 0.8);
    }
    cam.empurra(0.8, 0, 0, 1.0).empurra(comp.duration - 0.2, 0, 0, 1.06);
    fecha(comp);
}

// ---------------------------------------------------------------- 22. parede de elementos reais correndo
function cenaParedeLogos(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12, r, c;
    var rows = v ? 5 : 4, tile = Math.min(W, H) * (v ? 0.19 : 0.2), gapx = tile * 1.35, ncol = Math.ceil((W * 1.6) / gapx) + 1;
    var par = grupo(comp, { x: g.cx, y: g.cy, parent: cam.no, nome: 'parede' });
    var k = 0;
    for (r = 0; r < rows; r++) {
        var y = (r - (rows - 1) / 2) * tile * 1.45, dir = r % 2 ? 1 : -1, linha = grupo(comp, { x: 0, y: y, parent: par, nome: 'linha' });
        kP(linha).expression = 'value + [' + dir + ' * time * ' + (60 + r * 14).toFixed(0) + ' * ' + u.toFixed(2) + ', 0]';
        curva(kO(linha), [[T0 + r * 0.08, 0, null, 30], [T0 + r * 0.08 + 0.3, 100, 60, null]]);
        for (c = -Math.ceil(ncol / 2); c <= Math.ceil(ncol / 2); c++) {
            var cardx = c * gapx + (r % 2 ? gapx / 2 : 0);
            forma(comp, 'retangulo', { x: cardx, y: 0, w: tile, h: tile, r: tile * 0.26, cor: P.card, traco: P.mudo, tracoL: 2 * u, parent: linha, nome: 'tile' });
            elemento(comp, k, { x: cardx, y: 0, w: tile * 0.66, parent: linha });
            k++;
        }
    }
    kR(par).setValue(-8);
    var pw0 = W * (v ? 0.88 : 0.62), ph0 = H * (v ? 0.2 : 0.26);
    var plaquinha = forma(comp, 'retangulo', { x: g.cx, y: g.cy, w: pw0, h: ph0, r: 48 * u, cor: P.bg, traco: P.ac, tracoL: 8 * u, parent: cam.no, nome: 'faixa' });
    var n0 = comp.numLayers;
    var tt = kT(g, comp, P, d.titulo || '', g.cy, { maxLinhas: 2, tam: v ? 84 : 72, t0: T0 + 0.8, largura: W * (v ? 0.8 : 0.56), sobrepoe: true });
    // texto dentro de cartão, pela guarda: cabe com folga; senão o corpo encolhe até o mínimo; senão a faixa cresce (o texto
    // principal ganha linha e "PrimoCode" e "de bolso" vazavam da faixa fixa)
    if (tt && tt.h) {
        var tc = [tt.x - tt.w / 2, tt.y - tt.h / 2, tt.x + tt.w / 2, tt.y + tt.h / 2], pc = [g.cx - pw0 / 2, g.cy - ph0 / 2, g.cx + pw0 / 2, g.cy + ph0 / 2];
        var tMin = (H / W > 1.6 ? 112 : H * 0.036) * KIT.ajuste * KIT.ajusteCena;   // o piso do texto() (principal no 9:16, 3,6% fora)
        var gc = gTextoNoCartao(tc, pc, { folga: 55 * u, kMin: Math.min(1, tMin / Math.max(1, tt.tam)), max: [W * 0.03, H * 0.1, W * 0.97, H * 0.9] });
        if (gc.k < 0.999) {   // o corpo encolhe em volta do centro do bloco: cada camada criada pelo título (palavras, sublinhado)
            var j0, cxT = tt.x, cyT = tt.y;
            for (j0 = 1; j0 <= comp.numLayers - n0; j0++) { var Lt = comp.layer(j0), pv = kP(Lt).valueAtTime(comp.duration, false); kDesloca(Lt, (pv[0] - cxT) * (gc.k - 1), (pv[1] - cyT) * (gc.k - 1), gc.k); }
        }
        if (gc.cresceu) {
            var pw = gW(gc.cartao), ph = gH(gc.cartao);
            try { plaquinha.property('ADBE Root Vectors Group').property(1).property('ADBE Vectors Group').property('ADBE Vector Shape - Rect').property('ADBE Vector Rect Size').setValue([pw, ph]); }
            catch (e) { kS(plaquinha).setValue([100 * pw / pw0, 100 * ph / ph0]); }
            kP(plaquinha).setValue([gCx(gc.cartao), gCy(gc.cartao)]);
        }
        if (gc.log.length) writeLn('GUARDA ' + JSON.stringify({ cena: KIT.cenaIdx, w: W, h: H, log: gc.log, caixas: [{ tipo: 'cartao', nome: 'faixa do título', c: [Math.round(gc.cartao[0]), Math.round(gc.cartao[1]), Math.round(gc.cartao[2]), Math.round(gc.cartao[3])], fora: 0 }], violacoes: gc.ok ? [] : ['título maior que a faixa máxima'] }));
    }
    kEntrada(plaquinha, T0 + 0.7, 'pop', 0.5);
    som(T0, 'sobe', 0.5); som(T0 + 0.7, 'impacto', 0.7);
    cam.empurra(0.8, 0, 0, 1.0).empurra(comp.duration - 0.2, 0, 0, 1.08);
    fecha(comp);
}

// ---------------------------------------------------------------- 23. prompt digitado → gera → resultado (PrimoCode)
function cenaPrompt(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12;
    if (d.titulo) kT(g, comp, P, d.titulo, v ? H * 0.13 : H * 0.14, { maxLinhas: 2, tam: v ? 84 : 66 });
    var pw = v ? W * 0.88 : W * 0.62, ph = v ? H * 0.5 : H * 0.62, px = g.cx, py = v ? H * 0.56 : H * 0.58;
    var jan = forma(comp, 'retangulo', { x: px, y: py, w: pw, h: ph, r: 40 * u, cor: P.card, traco: P.mudo, tracoL: 3 * u, parent: cam.no, entrada: 'cresce', t0: T0, nome: 'janela' });
    var iw = pw * 0.9, ih = 120 * u, iy = py - ph / 2 + 100 * u;
    forma(comp, 'retangulo', { x: px, y: iy, w: iw, h: ih, r: 28 * u, cor: P.bg, traco: P.ac, tracoL: 4 * u, parent: cam.no, entrada: 'pop', t0: T0 + 0.2, nome: 'prompt' });
    var tc = T0 + 0.45, txt = d.texto || d.sub || 'Quero um app que faça isso';
    var dg = digita(comp, txt, { x: px - iw / 2 + 30 * u, y: iy, tamanho: 38 * u, fonte: P.fonteTexto, cor: P.fg, largura: iw - 160 * u, parent: cam.no, t0: tc, cps: 30 });
    var env = forma(comp, 'circulo', { x: px + iw / 2 - 56 * u, y: iy, w: 70 * u, cor: P.ac, parent: cam.no, entrada: 'pop', t0: T0 + 0.35, nome: 'enviar' });
    var t2 = Math.max(dg.fim + 0.15, tc + 1.0);
    curva(kS(env), [[t2, [100, 100], null, 25], [t2 + 0.07, [84, 84], 70, 40], [t2 + 0.25, [100, 100], 70, null]]); som(t2, 'clique', 0.9);
    // barra de progresso "gerando"
    var by = iy + ih / 2 + 60 * u, bw = iw * 0.9;
    forma(comp, 'pilula', { x: px, y: by, w: bw, h: 22 * u, cor: P.bg, parent: cam.no, entrada: 'fade', t0: t2 + 0.1, nome: 'trilho-prog' });
    var pg = forma(comp, 'pilula', { x: px - bw / 2, y: by, w: bw, h: 22 * u, cor: P.ac, parent: cam.no, nome: 'progresso' }); kA(pg).setValue([-bw / 2, 0]);
    curva(kS(pg), [[t2 + 0.15, [0, 100], null, 20], [t2 + 0.85, [100, 100], 70, null]]);
    curva(kO(pg), [[t2 + 0.1, 0, null, 30], [t2 + 0.15, 100, null, 30]]);
    som(t2 + 0.2, 'sobe', 0.6);
    // o resultado: um mini app que pula
    var it = kItens(d, ['Agenda', 'Clientes', 'Pagamentos'], 3), tr = t2 + 0.8, n = it.length, i, rh = (ph - (by - py + ph / 2) - 60 * u) / n - 14 * u;
    for (i = 0; i < n; i++) {
        var ry = by + 70 * u + rh / 2 + i * (rh + 14 * u), ti = Q(it[i], tr + i * 0.2);
        ti = Math.max(ti, tr);
        forma(comp, 'retangulo', { x: px, y: ry, w: iw, h: rh, r: 26 * u, cor: i == 0 ? kCardAc(P) : P.bg, parent: cam.no, entrada: 'pop', t0: ti, quique: 0.7, nome: 'resultado' });
        if (v) elemento(comp, i, { x: px - iw / 2 + rh * 0.7, y: ry, w: rh * 0.7, parent: cam.no, entrada: 'pop', t0: ti + 0.06 });   // em 16:9 a linha é baixa: só o texto e o check (ícone de 12% da largura não cabe)
        texto(comp, it[i], { tamanho: Math.min(40 * u, rh * 0.45), cor: i == 0 ? kSobreCard(P) : P.fg, corDestaque: P.ac, x: px + rh * 0.3, y: ry, parent: cam.no, entrada: 'sobe', t0: ti + 0.1, largura: iw - rh * 2, maxLinhas: 1, sublinhado: false, fala: false });
        check(comp, { x: px + iw / 2 - rh * 0.6, y: ry, tam: rh * 0.34, cor: i == 0 ? kSobreCard(P) : P.ac, parent: cam.no, t0: ti + 0.25 });
        som(ti, 'notif', 0.6);
    }
    cam.empurra(0.8, 0, 0, 1.02).empurra(tr, 0, 20 * u, 1.06).empurra(comp.duration - 0.2, 0, 30 * u, 1.1);
    fecha(comp);
}

// ---------------------------------------------------------------- 24. odômetro de palavras
function cenaOdometro(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, v = g.v, T0 = 0.12, i;
    fundo(comp, P.bg, {}); var cam = camera(comp);
    var it = kItens(d, ['rápido', 'simples', 'seu'], 6), n = it.length, tam = (v ? 150 : 120) * u, lh = tam * 1.5;
    var cy = v ? H * 0.46 : g.cy;
    var slot = grupo(comp, { x: g.cx, y: cy, parent: cam.no, nome: 'slot' });
    var cor;
    for (i = 0; i < n; i++) {
        var ult = i == n - 1;
        texto(comp, (ult ? '*' : '') + it[i], { tamanho: tam, cor: i % 2 ? P.mudo : P.fg, corDestaque: P.ac, x: 0, y: i * lh, parent: slot, entrada: null, t0: 0, largura: W * 0.84, maxLinhas: 1, sublinhado: false, fala: false, livre: true, entrada: 'fade' });
    }
    // o slot rola: cada parada com quique, a última cai no destaque
    var tr = Q(it[n - 1], T0 + 0.5 + 0.3 * n), passo = Math.max(0.2, Math.min(0.45, (tr - T0 - 0.3) / Math.max(1, n - 1)));
    for (i = 1; i < n; i++) {
        var ti = T0 + 0.3 + (i - 1) * passo; if (i == n - 1) ti = Math.max(ti, tr - 0.35);
        mola(kP(slot), ti, [g.cx, cy - (i - 1) * lh], [g.cx, cy - i * lh], { quique: i == n - 1 ? 0.6 : 0.2, dur: i == n - 1 ? 0.7 : 0.4 });
        som(ti + 0.1, i == n - 1 ? 'impacto' : 'tique', i == n - 1 ? 0.8 : 0.5);
    }
    kP(slot).setValueAtTime(0, [g.cx, cy]);
    // máscaras de cima e de baixo escondem o resto da fita
    var jan = lh * 0.62;
    forma(comp, 'retangulo', { x: g.cx, y: cy - jan - H, w: W * 1.2, h: H * 2, cor: P.bg, parent: cam.no, nome: 'tampa-cima' });
    forma(comp, 'retangulo', { x: g.cx, y: cy + jan + H, w: W * 1.2, h: H * 2, cor: P.bg, parent: cam.no, nome: 'tampa-baixo' });
    forma(comp, 'pilula', { x: g.cx, y: cy - jan, w: W * 0.8, h: 5 * u, cor: P.mudo, alfa: 50, parent: cam.no, nome: 'guia' });
    forma(comp, 'pilula', { x: g.cx, y: cy + jan, w: W * 0.8, h: 5 * u, cor: P.mudo, alfa: 50, parent: cam.no, nome: 'guia' });
        if (d.titulo) texto(comp, d.titulo, { tamanho: (v ? 90 : 76) * u, cor: P.fg, corDestaque: P.ac, x: g.cx, y: cy - lh * 1.1, parent: cam.no, entrada: 'sobe', t0: Q(d.titulo, T0), largura: W * 0.84, maxLinhas: 2, sublinhado: false });
    if (d.sub) texto(comp, d.sub, { fonte: P.fonteTexto, tamanho: 52 * u, cor: P.mudo, x: g.cx, y: cy + lh * 1.1 + 40 * u, parent: cam.no, entrada: 'sobe', t0: Q(d.sub, tr + 0.2), largura: W * 0.8, maxLinhas: 2, sublinhado: false });
    cam.empurra(0.8, 0, 0, 1.0).empurra(comp.duration - 0.2, 0, 0, 1.06);
    fecha(comp);
}

// ---------------------------------------------------------------- 25. medidor (gauge)
function cenaMedidor(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12, i;
    var cx = v ? g.cx : W * 0.64, cy = v ? H * 0.44 : H * 0.62, R = Math.min(W, H) * (v ? 0.36 : 0.34);
    if (d.titulo) kT(g, comp, P, d.titulo, v ? H * 0.15 : g.cy, { x: v ? g.cx : W * 0.22, largura: v ? W * 0.86 : W * 0.36, maxLinhas: 3, tam: v ? 88 : 72 });
    var trilho = forma(comp, 'circulo', { x: cx, y: cy, w: R * 2, traco: P.card, tracoL: 40 * u, parent: cam.no, nome: 'trilho' });
    kR(trilho).setValue(-90);
    var gt = trilho.property('ADBE Root Vectors Group').property(1).property('ADBE Vectors Group'), tt = gt.addProperty('ADBE Vector Filter - Trim'); tt.property('ADBE Vector Trim End').setValue(50);
    var prog = forma(comp, 'circulo', { x: cx, y: cy, w: R * 2, traco: P.ac, tracoL: 40 * u, parent: cam.no, nome: 'valor' });
    kR(prog).setValue(-90);
    var gp = prog.property('ADBE Root Vectors Group').property(1).property('ADBE Vectors Group'), tp = gp.addProperty('ADBE Vector Filter - Trim');
    var alvo = Math.max(0.2, Math.min(0.95, parseFloat(String(d.alvo || '0.82')) || 0.82)), t1 = T0 + 0.5, dd = 1.3;
    curva(tp.property('ADBE Vector Trim End'), [[t1, 0, null, 18], [t1 + dd, alvo * 50, 85, null]]);
    for (i = 0; i <= 10; i++) { var a = Math.PI + i * Math.PI / 10; var tk = forma(comp, 'retangulo', { x: cx + Math.cos(a) * (R + 46 * u), y: cy + Math.sin(a) * (R + 46 * u), w: 6 * u, h: 22 * u, cor: P.mudo, parent: cam.no, nome: 'marca', entrada: 'fade', t0: T0 + i * 0.03 }); kR(tk).setValue(i * 18 - 90); }
    var agulha = forma(comp, 'pilula', { x: cx, y: cy, w: 16 * u, h: R * 0.9, cor: P.fg, parent: cam.no, nome: 'agulha' }); kA(agulha).setValue([0, R * 0.45]);
    var ang = -90 + alvo * 180;
    curva(kR(agulha), [[t1, -90, null, 18], [t1 + dd * 0.85, ang + 8, 80, 30], [t1 + dd, ang, 70, null]]);
    forma(comp, 'circulo', { x: cx, y: cy, w: 46 * u, cor: P.ac, parent: cam.no, nome: 'eixo' });
    som(t1, 'sobe', 0.6); som(t1 + dd, 'impacto', 0.8);
    var vt = d.valor || '82%';
    texto(comp, vt, { fonte: P.fonteTitulo, tamanho: (v ? 190 : 150) * u, cor: P.fg, corDestaque: P.ac, x: cx, y: cy + R * 0.42, parent: cam.no, entrada: 'palavra', t0: Math.max(t1 + dd - 0.1, Q(vt, t1 + dd)), largura: R * 1.6, maxLinhas: 1, sublinhado: false, fala: false, livre: true });
    if (d.rotulo) texto(comp, d.rotulo, { fonte: P.fonteTexto, tamanho: 46 * u, cor: P.mudo, x: cx, y: cy + R * 0.42 + 115 * u, parent: cam.no, entrada: 'sobe', t0: Q(d.rotulo, t1 + dd + 0.2), largura: R * 1.8, maxLinhas: 2, sublinhado: false });
    kEstouro(comp, cam, cx, cy, t1 + dd, P.ac, 10, R * 1.1, false);
    cam.empurra(0.8, 0, 0, 1.0).empurra(t1 + dd, 0, -10 * u, 1.07).empurra(comp.duration - 0.2, 0, 0, 1.1);
    fecha(comp);
}

// ---------------------------------------------------------------- 26. carimbo que bate no papel
function cenaCarimbo(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12, i;
    var pw = v ? W * 0.78 : W * 0.42, ph = v ? H * 0.5 : H * 0.8, px = g.cx, py = v ? H * 0.46 : g.cy;
    var papel = forma(comp, 'retangulo', { x: px, y: py, w: pw, h: ph, r: 14 * u, cor: kLum(P.bg) > 0.5 ? '#FFFFFF' : '#EDEAE2', parent: cam.no, nome: 'papel' });
    mola(kP(papel), T0, [px, py + H * 0.5], [px, py], { quique: 0.4, dur: 0.8 }); mola(kR(papel), T0, 6, -2, { quique: 0.5, dur: 0.9 });
    som(T0 + 0.1, 'papel', 0.8);
    var it = kItens(d, ['Nome do cliente', 'Serviço contratado', 'Prazo combinado'], 4), n = it.length;
    for (i = 0; i < n; i++) {
        var y = py - ph * 0.3 + i * ph * 0.11, tl = T0 + 0.5 + i * 0.2;
        var ln = forma(comp, 'pilula', { x: px, y: y, w: pw * 0.74, h: 12 * u, cor: '#C9C4B8', parent: papel, nome: 'linha' });
        kP(ln).setValue([0, y - py]);
        curva(kS(ln), [[tl, [0, 100], null, 25], [tl + 0.4, [100, 100], 75, null]]);
    }
    var ts = Q(d.titulo || '', T0 + 1.3), cs = P.ac, sx = px + pw * 0.08, sy = py + ph * 0.12, sz = Math.min(pw * 0.72, 520 * u);
    var st = grupo(comp, { x: sx, y: sy, parent: cam.no, nome: 'carimbo' });
    forma(comp, 'circulo', { x: 0, y: 0, w: sz, traco: cs, tracoL: 14 * u, parent: st, nome: 'selo-anel' });
    forma(comp, 'circulo', { x: 0, y: 0, w: sz * 0.86, traco: cs, tracoL: 4 * u, parent: st, nome: 'selo-anel2' });
    texto(comp, String(d.titulo || 'APROVADO').replace(/\*/g, '').toUpperCase(), { fonte: P.fonteTitulo, tamanho: 110 * u, cor: cs, corDestaque: cs, x: 0, y: 0, parent: st, entrada: null, t0: 0, largura: sz * 0.74, maxLinhas: 2, sublinhado: false, fala: false, livre: true, caixa: false });
    kR(st).setValue(-12);
    // a batida: de grande e transparente para o lugar, com tremor no papel e na câmera
    curva(kS(st), [[ts, [260, 260], null, 14], [ts + 0.14, [100, 100], 40, 40], [ts + 0.2, [106, 106], 70, 40], [ts + 0.5, [100, 100], 70, null]]);
    curva(kO(st), [[ts - 0.04, 0, null, 30], [ts, 0, null, 30], [ts + 0.1, 100, 50, null]]);
    curva(kP(papel), [[ts + 0.14, [px, py], null, null], [ts + 0.17, [px + 14 * u, py + 10 * u], -1, -1], [ts + 0.22, [px - 12 * u, py - 6 * u], -1, -1], [ts + 0.3, [px, py], 60, null]]);
    som(ts + 0.12, 'impacto', 1.0); som(ts + 0.12, 'papel', 0.5);
    kEstouro(comp, cam, sx, sy, ts + 0.14, cs, 12, sz * 0.7, false);
    if (d.rotulo) texto(comp, d.rotulo, { fonte: P.fonteTexto, tamanho: 50 * u, cor: P.fg, corDestaque: P.ac, x: g.cx, y: py + ph / 2 + 80 * u, parent: cam.no, entrada: 'sobe', t0: Q(d.rotulo, ts + 0.5), largura: W * 0.8, maxLinhas: 2, sublinhado: false });
    cam.empurra(0.8, 0, 0, 1.0).empurra(ts + 0.15, 0, 0, 1.08).empurra(comp.duration - 0.2, 0, -10 * u, 1.1);
    fecha(comp);
}

// ---------------------------------------------------------------- 27. leque de cartas
function cenaLeque(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12, i;
    var it = kItens(d, ['Um', 'Dois', 'Três'], 5), n = it.length;
    if (d.titulo) kT(g, comp, P, d.titulo, v ? H * 0.14 : H * 0.18, { maxLinhas: 2, tam: v ? 86 : 70, sobrepoe: true });
    var px = g.cx, py = v ? H * 0.86 : H * 1.05, cw = v ? W * 0.42 : W * 0.22, ch = cw * 1.45, spread = v ? 14 : 11;
    for (i = 0; i < n; i++) {
        var ang = (i - (n - 1) / 2) * spread, ti = Q(it[i], T0 + 0.35 + i * 0.28), ult = i == n - 1;
        var carta = grupo(comp, { x: px, y: py, parent: cam.no, nome: 'carta' });
        kA(carta).setValue([0, 0]);
        var c = forma(comp, 'retangulo', { x: 0, y: -ch * 0.78, w: cw, h: ch, r: 34 * u, cor: ult ? kCardAc(P) : P.card, traco: ult ? null : P.mudo, tracoL: 3 * u, parent: carta, nome: 'carta-corpo' });
        elemento(comp, i, { x: 0, y: -ch * 0.95, w: cw * 0.6, parent: carta });
        texto(comp, it[i], { fonte: P.fonteTexto, tamanho: 36 * u, cor: ult ? kSobreCard(P) : P.fg, corDestaque: P.ac, x: 0, y: -ch * 0.52, parent: carta, entrada: 'fade', t0: ti, largura: cw * 0.84, maxLinhas: 2, sublinhado: false, fala: false, livre: true });
        mola(kR(carta), ti - 0.1, 0, ang, { quique: 0.55, dur: 0.8 });
        curva(kO(carta), [[ti - 0.14, 0, null, 30], [ti - 0.1, 100, null, 30]]);
        som(ti, 'papel', 0.6);
        if (ult) { curva(kP(carta), [[ti + 0.7, [px, py], null, 25], [ti + 1.0, [px, py - ch * 0.18], 75, null]]); som(ti + 0.7, 'brilho', 0.6); }
    }
    cam.empurra(0.8, 0, 0, 1.0).empurra(comp.duration - 0.2, 0, -30 * u, 1.08);
    fecha(comp);
}

// ---------------------------------------------------------------- 28. ondas de áudio
function cenaOndas(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12, i;
    var nb = 29, bw = Math.min(W * 0.8 / nb * 0.6, 34 * u), gap = W * 0.8 / nb, cy = v ? H * 0.4 : g.cy - H * 0.06, mh = Math.min(H * 0.3, 420 * u);
    for (i = 0; i < nb; i++) {
        var x = g.cx + (i - (nb - 1) / 2) * gap, env = Math.exp(-Math.pow((i - (nb - 1) / 2) / (nb * 0.32), 2));
        var b = forma(comp, 'pilula', { x: x, y: cy, w: bw, h: mh, cor: i % 5 == 2 ? P.fg : P.ac, parent: cam.no, nome: 'onda' });
        kS(b).expression = 'var a = 0.14 + 0.86 * ' + env.toFixed(2) + ' * Math.abs(Math.sin(time * ' + (3.1 + (i % 7) * 0.55).toFixed(2) + ' + ' + (i * 1.3).toFixed(2) + ') * Math.cos(time * ' + (1.7 + (i % 5) * 0.4).toFixed(2) + ' + ' + i + ')); var e = Math.min(1, time / 0.5); [100, 100 * a * e]';
        curva(kO(b), [[T0 + Math.abs(i - nb / 2) * 0.015, 0, null, 30], [T0 + 0.25 + Math.abs(i - nb / 2) * 0.015, 100, 60, null]]);
    }
    som(T0, 'sobe', 0.4);
    if (d.titulo) kT(g, comp, P, d.titulo, v ? H * 0.68 : g.cy + H * 0.3, { maxLinhas: 3, tam: v ? 90 : 72, t0: T0 + 0.5 });
    elemento(comp, 0, { x: g.cx, y: v ? H * 0.16 : H * 0.12, w: 190 * u, parent: cam.no, entrada: 'pop', t0: T0 + 0.1, flutua: 8 });
    cam.empurra(0.8, 0, 0, 1.02).empurra(comp.duration - 0.2, 0, 0, 1.08);
    fecha(comp);
}

// ---------------------------------------------------------------- 29. rolagem de feed
function cenaRolagem(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12, i;
    var it = kItens(d, ['Post um', 'Post dois', 'Post três'], 5), n = it.length;
    var cw = v ? W * 0.8 : W * 0.38, ch = v ? H * 0.2 : H * 0.28, gap = 28 * u, x = v ? g.cx : W * 0.7;
    var feed = grupo(comp, { x: x, y: 0, parent: cam.no, nome: 'feed' });
    var total = n * (ch + gap), y0 = v ? H * 0.3 : H * 0.14, tl = T0 + 0.2, dd = 1.3;
    // no 9:16 com título, o feed sobe UM cartão: começa um cartão abaixo do topo do conteúdo (Ct do kT) pra nunca passar por baixo do título
    if (v && d.titulo) y0 = H * 0.2 + gap + (ch + gap);
    for (i = 0; i < n; i++) {   // um post por item: nada de repetir o primeiro no fim pra encher o feed
        var y = y0 + i * (ch + gap) + ch / 2, idx = i;
        forma(comp, 'retangulo', { x: 0, y: y, w: cw, h: ch, r: 36 * u, cor: P.card, traco: P.mudo, tracoL: 3 * u, parent: feed, nome: 'post' });
        elemento(comp, i, { x: -cw / 2 + ch * 0.55, y: y, w: ch * 0.7, parent: feed });
        texto(comp, it[idx], { tamanho: Math.min(44 * u, ch * 0.28), cor: P.fg, corDestaque: P.ac, x: ch * 0.28, y: y - ch * 0.12, parent: feed, entrada: 'fade', t0: T0, largura: cw - ch * 1.5, maxLinhas: 2, sublinhado: false, fala: false, livre: true });
        forma(comp, 'pilula', { x: ch * 0.28 - (cw - ch * 1.6) * 0.18, y: y + ch * 0.28, w: (cw - ch * 1.6) * 0.6, h: 12 * u, cor: P.mudo, alfa: 45, parent: feed, nome: 'linha-skel' });
        var cor = forma(comp, 'circulo', { x: cw / 2 - 52 * u, y: y - ch * 0.28, w: 40 * u, cor: P.ac, parent: feed, nome: 'coracao', alfa: 0 });
        var tco = tl + dd * (i + 0.7) / (n + 0.6);
        curva(kO(cor), [[tco, 0, null, 30], [tco + 0.1, 100, 60, null]]); mola(kS(cor), tco, [0, 0], [100, 100], { quique: 0.8, dur: 0.5 });
        if (i < n) som(tco, 'pop', 0.5);
    }
    curva(kP(feed), [[tl, [x, 0], null, 20], [tl + dd, [x, -(ch + gap) * 1.0], 80, null]]);
    forma(comp, 'retangulo', { x: x - cw / 2 - 20 * u, y: g.cy, w: 8 * u, h: H * 0.5, r: 4 * u, cor: P.ac, parent: cam.no, entrada: 'fade', t0: T0, nome: 'barra-rolagem' });
    if (d.titulo) kT(g, comp, P, d.titulo, v ? H * 0.13 : g.cy, { x: v ? g.cx : W * 0.24, largura: v ? W * 0.86 : W * 0.36, maxLinhas: 3, tam: v ? 84 : 70, sobrepoe: true });
    som(tl, 'sobe', 0.4);
    cam.empurra(0.8, 0, 0, 1.02).empurra(comp.duration - 0.2, 0, 0, 1.06);
    fecha(comp);
}

// ---------------------------------------------------------------- 30. prova social (estrelas + depoimento)
function cenaEstrelas(comp, P, d) {
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.12, i;
    var cw = v ? W * 0.84 : W * 0.56, ch = v ? H * 0.5 : H * 0.7, cx = g.cx, cy = g.cy;
    var card = forma(comp, 'retangulo', { x: cx, y: cy, w: cw, h: ch, r: 54 * u, cor: P.card, traco: P.mudo, tracoL: 3 * u, parent: cam.no, entrada: 'cresce', t0: T0, nome: 'cartao' });
    var ey = cy - ch * 0.34, es = 74 * u;
    for (i = 0; i < 5; i++) {
        var ti = T0 + 0.4 + i * 0.16, sx = cx + (i - 2) * es * 1.15;
        var est = caminho(comp, kEstrelaPts(es * 0.5, es * 0.22), { preenche: P.ac, fecha: true, parent: cam.no, nome: 'estrela' });
        kP(est).setValue([sx, ey]);
        mola(kS(est), ti, [0, 0], [100, 100], { quique: 0.8, dur: 0.55 }); mola(kR(est), ti, -40, 0, { quique: 0.5, dur: 0.6 });
        som(ti, 'ponto' + (1 + i % 5), 0.6);
    }
    var nota = d.valor || '4,9';
    texto(comp, nota, { fonte: P.fonteTitulo, tamanho: 130 * u, cor: P.fg, corDestaque: P.ac, x: cx, y: ey + es * 1.5, parent: cam.no, entrada: 'palavra', t0: Math.max(T0 + 1.3, Q(nota, T0 + 1.3)), largura: cw * 0.5, maxLinhas: 1, sublinhado: false, fala: false });
    var tx = d.texto || d.titulo || '';
    texto(comp, tx, { tamanho: (v ? 52 : 46) * u, cor: P.fg, corDestaque: P.ac, x: cx, y: cy + ch * 0.12, parent: cam.no, entrada: 'palavra', t0: Q(tx, T0 + 1.7), passo: 0.12, largura: cw * 0.82, maxLinhas: 5 });
    if (d.autor) {
        elemento(comp, 0, { x: cx - cw * 0.3, y: cy + ch * 0.38, w: 90 * u, parent: cam.no, entrada: 'pop', t0: T0 + 2.0 });
        texto(comp, d.autor, { fonte: P.fonteTexto, tamanho: 40 * u, cor: P.mudo, x: cx + 30 * u, y: cy + ch * 0.38, parent: cam.no, entrada: 'sobe', t0: T0 + 2.1, largura: cw * 0.5, maxLinhas: 1, sublinhado: false, fala: false });
    }
    som(T0 + 1.3, 'brilho', 0.6);
    cam.empurra(0.8, 0, 0, 1.02).empurra(comp.duration - 0.2, 0, 0, 1.08);
    fecha(comp);
}

// ---------------------------------------------------------------- 31. foto: a foto do pedido em 3 planos, em 3D, com passeio de câmera
/* d.foto = {arquivo, fundo, recorte, w, h, moldura} (o Node já copiou pro projeto, desfocou o fundo e tentou o recorte);
   d.movimento (aproxima · passeio · revela · orbita · afasta) é o que o diretor sorteou a partir da câmera da cena, e
   d.sentido (±1) alterna entre fotos seguidas. Planos: 1) o fundo desfocado e ampliado, 2) a foto (recorte com alfa, ou a foto
   inteira numa moldura arredondada que é uma comp própria: o passeio acontece DENTRO da moldura), em 3D com rotação,
   3) elementos reais na frente, que andam mais que a foto (parallax). Tudo com ease: nada linear. */
function kFotoImp(arq) {
    if (!arq) return null;
    KIT.cache = KIT.cache || {};
    if (KIT.cache[arq]) return KIT.cache[arq];
    try { var it = app.project.importFile(new ImportOptions(new File(arq))); KIT.cache[arq] = it; return it; }
    catch (er) { KIT.avisos.push('cena ' + KIT.cenaIdx + ': a foto não abriu: ' + arq); return null; }
}
function kFotoPonto(cw, ch, pan, z, fx, fy) { var lim = (z - 1) / 2 * pan; return [cw / 2 - fx * lim * cw, ch / 2 - fy * lim * ch]; }
function kRot(L, eixo) { return L.property('ADBE Transform Group').property('ADBE Rotate ' + eixo); }
function cenaFoto(comp, P, d) {
    var F = d.foto, itF = F ? kFotoImp(F.recorte || F.arquivo) : null;
    if (!itF) { KIT.avisos.push('cena ' + KIT.cenaIdx + ': sem foto, a cena virou herói'); return cenaHeroi(comp, P, d); }
    var g = kPre(comp, P), W = g.W, H = g.H, u = g.u, cam = g.cam, v = g.v, T0 = 0.1, i, dur = comp.duration;
    var mov = d.movimento || 'aproxima', sg = d.sentido || 1, recorte = !!F.recorte;
    var ease = [60, 60];
    // 1. o plano de trás: a própria foto desfocada, ampliada, com um véu da cor da cena; anda ao contrário da foto
    var itB = F.fundo ? kFotoImp(F.fundo) : null;
    if (itB) {
        var bg = comp.layers.add(itB); bg.name = 'foto-fundo';   // sem pai: a câmera encolhe o quadro (88%) e deixaria borda; o fundo tem o movimento dele
        var kb = Math.max(W / itB.width, H / itB.height) * 100;
        kP(bg).setValue([g.cx, g.cy]);
        curva(kS(bg), [[0, [kb * 1.32, kb * 1.32], ease[0], ease[1]], [dur, [kb * 1.5, kb * 1.5], ease[0], ease[1]]]);
        curva(kP(bg), [[0, [g.cx + sg * W * 0.035, g.cy], ease[0], ease[1]], [dur, [g.cx - sg * W * 0.035, g.cy - H * 0.01], ease[0], ease[1]]]);
        var veu = comp.layers.addSolid(kCor(P.bg), 'foto-veu', W, H, 1); kO(veu).setValue(40);
    }
    // 2. a foto: o tamanho da caixa respeita a proporção dela (nada cortado) e a zona do texto
    var fw = recorte ? (F.rw || F.w) : F.w, fh = recorte ? (F.rh || F.h) : F.h;   // o recorte já vem aparado: só a pessoa
    var LY = d.layout || null;   // o plano da GUARDA (maquina-guarda.js): foto grande, título sem encostar, ícones só no espaço livre
    var bx = v ? W * (recorte ? 0.9 : 0.8) : W * 0.4, by = v ? H * (recorte ? 0.5 : 0.46) : H * (recorte ? 0.84 : 0.74);
    var cwf = Math.min(bx, by * fw / fh), chf = cwf * fh / fw;
    var px = v ? g.cx : W * 0.3, py = recorte ? (v ? H * 0.64 - chf / 2 : H * 0.9 - chf / 2) : (v ? H * 0.38 : g.cy);
    var corte = 1;   // a parte da altura que aparece (foto alta cortada em 4:5 pela guarda)
    if (LY && LY.foto) { cwf = LY.foto.w; chf = LY.foto.h; px = LY.foto.x; py = LY.foto.y; corte = LY.foto.corte || 1; }
    var halo;
    if (LY && LY.halo && LY.halo.d) halo = forma(comp, 'circulo', { x: LY.halo.x, y: LY.halo.y, w: LY.halo.d, cor: P.ac, alfa: 70, parent: cam.no, entrada: 'cresce', t0: T0, nome: 'halo' });
    else if (LY && LY.halo) halo = forma(comp, 'retangulo', { x: LY.halo.x, y: LY.halo.y, w: LY.halo.w, h: LY.halo.h, r: 46 * u, cor: P.ac, alfa: 34, parent: cam.no, entrada: 'cresce', t0: T0, nome: 'halo' });
    else halo = forma(comp, 'circulo', { x: px, y: py, w: Math.max(cwf, chf) * (recorte ? 0.95 : 1.05), cor: P.ac, alfa: recorte ? 70 : 26, parent: cam.no, entrada: 'cresce', t0: T0, nome: 'halo' });
    var card, kBase0;
    if (recorte) {
        card = comp.layers.add(itF); card.name = 'foto-recorte'; kPai(card, cam.no);
        kBase0 = cwf / itF.width * 100;
        kA(card).setValue([itF.width / 2, itF.height * corte / 2]);
        // o corte da foto (ombros e braços na beira, ou o corpo abaixo do 4:5) some num degradê: a pessoa não termina numa linha reta
        try { var mk = card.property('ADBE Mask Parade').addProperty('ADBE Mask Atom'); mk.property('ADBE Mask Shape').setValue(kRetShape(itF.width * 0.02, -itF.height, itF.width * 0.98, itF.height * corte * 0.96, 0)); mk.property('ADBE Mask Feather').setValue([itF.width * 0.05, itF.height * corte * 0.12]); } catch (eM) { }
    } else {
        var inner = novaComp('foto-int ' + KIT.cenaIdx, Math.round(cwf), Math.round(chf), dur, KIT.fps);
        var im = inner.layers.add(itF); im.name = 'foto-img';
        var ki = Math.max(cwf / itF.width, chf / itF.height) * 100;
        kP(im).setValue([cwf / 2, chf / 2]);
        // o passeio DENTRO da moldura (ease, nunca linear): escala e deslocamento por movimento
        var z0 = 1, z1 = 1.2, x0 = 0, x1 = 0, y0 = 0, y1 = 0, pan = 0.8;
        if (mov == 'aproxima') { z0 = 1.0; z1 = 1.3; y0 = 0; y1 = -0.06; }
        else if (mov == 'passeio') { z0 = 1.26; z1 = 1.26; x0 = -sg * 0.5; x1 = sg * 0.5; y0 = 0.25; y1 = -0.25; }
        else if (mov == 'revela') { z0 = 1.45; z1 = 1.05; x0 = sg * 0.2; x1 = 0; y0 = 0.3; y1 = 0; }
        else if (mov == 'orbita') { z0 = 1.08; z1 = 1.22; x0 = -sg * 0.25; x1 = sg * 0.25; }
        else if (mov == 'afasta') { z0 = 1.34; z1 = 1.0; y0 = -0.12; y1 = 0.0; }
        curva(kS(im), [[0, [ki * z0, ki * z0], ease[0], ease[1]], [T0 + 0.9 * dur / 3, [ki * (z0 + (z1 - z0) * 0.5), ki * (z0 + (z1 - z0) * 0.5)], ease[0], ease[1]], [dur, [ki * z1, ki * z1], ease[0], ease[1]]]);
        curva(kP(im), [[0, kFotoPonto(cwf, chf, pan, z0, x0, y0), ease[0], ease[1]], [dur, kFotoPonto(cwf, chf, pan, z1, x1, y1), ease[0], ease[1]]]);
        card = comp.layers.add(inner); card.name = 'foto-moldura'; kPai(card, cam.no);
        kMascaraRect(card, 0, 0, cwf, chf, 38 * u);
        kBase0 = 100;
    }
    kP(card).setValue([px, py]);
    kS(card).setValue([kBase0, kBase0]);
    if (recorte) {   // o recorte anda e cresce no próprio plano (a foto inteira não tem moldura onde passear)
        // com a guarda (LY) o passeio é mais curto na escala e no deslocamento: a foto fica GRANDE e o 3D (rotação) leva o movimento
        var rzA = LY ? 0.97 : 0.94, rzB = LY ? 1.06 : 1.12, rzC = LY ? 1.06 : 1.1, dxF = LY ? 0.025 : 0.04, dyF = LY ? 0.015 : 0.03;
        var rz1 = mov == 'afasta' || mov == 'revela' ? rzA : rzC, rz0 = mov == 'afasta' || mov == 'revela' ? rzB : 1.0;
        curva(kS(card), [[0, [kBase0 * rz0, kBase0 * rz0], ease[0], ease[1]], [dur, [kBase0 * rz1, kBase0 * rz1], ease[0], ease[1]]]);
        if (mov == 'passeio' || mov == 'orbita') curva(kP(card), [[0, [px - sg * W * dxF, py], ease[0], ease[1]], [dur, [px + sg * W * dxF, py], ease[0], ease[1]]]);
        else curva(kP(card), [[0, [px, py + H * dyF], ease[0], ease[1]], [dur, [px, py - H * dyF / 3], ease[0], ease[1]]]);
    }
    // sombra no chão e entrada: a foto chega girando e assenta (a rotação é o 3D; a mola é a entrada)
    var sombra = LY ? sombraChao(comp, px, py + chf * 0.5 - 30 * u, cwf * 0.5, { parent: cam.no, t0: T0 + 0.25, alfa: 22 })   // com a guarda: a sombra fica no envelope da foto
        : sombraChao(comp, px, py + chf * 0.5 + 18 * u, cwf * 0.7, { parent: cam.no, t0: T0 + 0.25, alfa: 28 });
    curva(kO(card), [[T0, 0, null, 30], [T0 + 0.3, 100, 60, null]]);
    kMarcaEntrada(T0 + 0.3);
    try {
        card.threeDLayer = true;
        var A = (mov == 'aproxima') ? [[-22, -7, 5], [8, 3, -2]] : (mov == 'passeio') ? [[-12, 0, 0], [12, 4, 0]] : (mov == 'revela') ? [[60, 0, 16], [0, 0, 0]] : (mov == 'orbita') ? [[-34, 0, 4], [34, 0, -2]] : [[14, 0, -12], [0, 0, 0]];
        var tm = Math.min(dur * 0.55, T0 + 0.95);
        curva(kRot(card, 'Y'), [[0, A[0][0] * sg, ease[0], ease[1]], [mov == 'passeio' || mov == 'orbita' ? dur : tm, (mov == 'passeio' || mov == 'orbita' ? A[1][0] : (A[0][0] + A[1][0]) / 2 * 0.4) * sg, ease[0], ease[1]], [dur, A[1][0] * sg, ease[0], ease[1]]]);
        curva(kRot(card, 'X'), [[0, A[0][2], ease[0], ease[1]], [dur, A[1][2], ease[0], ease[1]]]);
    } catch (e) { KIT.avisos.push('cena ' + KIT.cenaIdx + ': 3D da foto indisponível: ' + e); }
    som(T0, 'impacto', 0.6); som(T0 + 0.5, 'sobe', 0.45);
    // 3. na frente: elementos reais do assunto, que andam mais que a foto (parallax)
    var nEl = Math.min(KIT.elementos.length, v ? 3 : 2), S = Math.min(W, H) * (v ? 0.2 : 0.24);
    var posV = [[W * 0.2, H * 0.17], [W * 0.84, H * 0.5], [W * 0.18, H * 0.58]], posH = [[W * 0.54, H * 0.2], [W * 0.5, H * 0.8]];
    var ics = [];
    if (LY && LY.icones) { for (i = 0; i < LY.icones.length; i++) if (!LY.icones[i].fora) ics.push({ i: LY.icones[i].i, x: LY.icones[i].x, y: LY.icones[i].y, w: LY.icones[i].w }); }
    else for (i = 0; i < nEl; i++) ics.push({ i: i, x: (v ? posV[i] : posH[i])[0], y: (v ? posV[i] : posH[i])[1], w: S });
    for (i = 0; i < ics.length; i++) {
        var ph = ics[i].i * 1.9, a = 12 * u, kx = (ics[i].i % 2 ? -1 : 1) * W * 0.05;
        // o envelope do balanço vai pra caixa (folga): a guarda do kit reserva o caminho inteiro do ícone
        var el = elemento(comp, ics[i].i, { x: ics[i].x, y: ics[i].y, w: ics[i].w, parent: cam.no, entrada: 'pop', t0: T0 + 0.55 + i * 0.18, quique: 0.7, folgaX: a + Math.abs(kx), folgaY: a * 1.1 });
        kP(el).expression = 'value + [Math.sin(time*1.7+' + ph.toFixed(1) + ')*' + a.toFixed(1) + ' + ' + (sg * kx).toFixed(1) + ' * (time / ' + dur.toFixed(2) + ' - 0.5) * 2, Math.cos(time*1.3+' + ph.toFixed(1) + ')*' + (a * 1.1).toFixed(1) + ']';
    }
    if (d.titulo) {
        var TY = LY && LY.titulo;
        kT(g, comp, P, d.titulo, TY ? TY.y : (v ? H * 0.79 : g.cy), { x: TY ? TY.x : (v ? g.cx : W * 0.73), largura: TY ? TY.largura : (v ? W * 0.86 : W * 0.42), maxLinhas: 3, tam: TY ? TY.tam : (v ? 86 : 74), t0: T0 + 0.7 });
    }
    // a foto entra na guarda do kit (fecha → kGuarda) com a caixa do envelope: se o texto real encostar, ela sobe ou encolhe
    if (LY && LY.particulas) {
        var ec = LY.particulas;
        KIT.rects.push({ espaco: 'c', cena: KIT.cenaIdx, x0: ec[0], y0: ec[1], x1: ec[2], y1: ec[3], t0: 0, t1: 1, texto: false, livre: true, sobrepoe: true, guarda: 'foto', nome: 'a foto', camadas: [card, halo, sombra], infla: (ec[2] - ec[0]) / Math.max(1, cwf) });
    }
    var pa = LY && LY.particulas ? [LY.particulas[0], LY.particulas[1], LY.particulas[2], LY.particulas[3]] : [px - cwf * 0.6, py - chf * 0.6, px + cwf * 0.6, py + chf * 0.5];
    particulas(comp, { n: 10, cor: P.ac, parent: cam.no, tipo: 'flutua', raio: 8, area: pa, alfa: 70, t0: T0 + 0.4 });
    cam.empurra(0.8, 0, 0, 1.0).empurra(comp.duration - 0.2, 0, 0, 1.06);
    fecha(comp);
}
