# Anatomia do quiz de casal — "qual o rolê perfeito pra vocês dois?"

> Doc de estudo do `quiz-casal` (Tour Curitiba). Escrito a partir do código-fonte real em
> `/root/quiz-casal/frontend/src` — `data/quiz.js`, `lib/scoring.js`,
> `screens/{QuizApp,Question,LeadGate,Result}.jsx`, `lib/api.js`.
>
> ⚠️ **Material interno.** Contém caminhos, endpoints, mecânica de produto e crítica de
> fraquezas. NÃO publicar em pasta pública (ex.: assets-api).

**Números-chave:** 4 perguntas · 3 arquétipos · 192 caminhos de resposta · 180 dates possíveis · 1 date sorteado.

---

## 1. O fluxo em cinco telas

Uma única máquina de estados em `QuizApp.jsx` controla tudo. O `step` caminha por:
`intro` → `0…3` (as perguntas) → `gate` → `result`. As respostas ficam num objeto `answers`
e a pontuação é derivada com `useMemo` só quando todas as 4 estão preenchidas.

| step | tela | o que faz |
|---|---|---|
| `"intro"` | abertura | pitch do quiz + botão começar |
| `0…3` | perguntas | seleção avança sozinha em 240 ms; dá pra voltar |
| `"gate"` | lead gate | nome · WhatsApp · e-mail (opc.) — trava o resultado |
| `"result"` | resultado | arquétipo + date de 2 paradas + CTAs |

**Detalhe de UX:** clicar numa opção já grava a resposta e, após um respiro de 240 ms pra
ver a marcação, pula pra próxima (ou pro gate na última). Não existe botão "avançar" — só
"← voltar". O resultado **só aparece depois do gate**.

---

## 2. As 4 perguntas e seus pesos

Cada opção carrega um objeto `p` com pontos por arquétipo. A maioria vale **3** pro
arquétipo "cheio"; algumas valem **2** (voto fraco) e uma **divide** o ponto. Definido em
`data/quiz.js › QUESTIONS`.

### q1 — "Vocês dois são mais de…"
| Opção | Pontos |
|---|---|
| 💞 Noite tranquila e a dois | romantico 3 |
| 🌎 Sair e experimentar coisa nova | aventureiro 3 |
| 🍻 Bar, petisco e muita risada | raiz 3 |
| 🍷 Comemorar com estilo | romantico 2 |

### q2 — "O date perfeito de vocês tem…"
| Opção | Pontos |
|---|---|
| 🍷 Jantar caprichado à luz de vela | romantico 3 |
| 🍣 Comida diferente pra dividir | aventureiro 3 |
| 🍔 Hambúrguer, cerveja e zero frescura | raiz 3 |
| 🍰 Um docinho pra fechar a noite | romantico 1 + raiz 1 |

### q3 — "A vibe de vocês é…"
| Opção | Pontos |
|---|---|
| 💞 Romântica e apaixonada | romantico 3 |
| 🌎 Curiosa e aventureira | aventureiro 3 |
| 🍻 Divertida e descontraída | raiz 3 |
| ✨ Surpresa, nunca repetem o mesmo | aventureiro 2 |

### q4 — "Domingo do casal é…" (só 3 opções)
| Opção | Pontos |
|---|---|
| 🥐 Brunch e preguiça juntinhos | romantico 2 |
| 🌎 Achar um lugar novo pra explorar | aventureiro 3 |
| 🍻 Parque, comida de rua e cerveja | raiz 3 |

**Três assimetrias que só aparecem lendo as 4 juntas:**
1. O **romantico** tem 6 "portas de entrada" (q1a, q1d, q2a, q2d, q3a, q4a) contra 5 dos outros.
2. Na q4 o romantico vale só **2**, não 3 — é o que segura o teto dele mais baixo.
3. A q2d ("docinho") divide ponto, então não é voto limpo de ninguém.

---

## 3. O motor de pontuação e desempate

Uma função só, em `lib/scoring.js`, idêntica à do `quiz-tour`.

```js
// lib/scoring.js — simplificado
for (const opt of Object.values(answers))
  for (const [k, v] of Object.entries(opt.p))
    totals[k] += v;              // acumula o placar

let best = TIE_ORDER[0];
for (const k of TIE_ORDER)
  if (totals[k] > totals[best]) best = k;   // > estrito → empate fica com quem vem antes
```

`best` começa no **primeiro** item de `TIE_ORDER` e só troca de líder se a pontuação for
**estritamente maior** — em empate ganha quem aparece **antes** no array.

### Ordem de desempate atual
```js
export const TIE_ORDER = ["raiz", "romantico", "aventureiro"];
```

Ajuste recente. Simulando as **192 combinações** (4×4×4×3), pesos intactos, só a ordem:

| Cenário | romantico | aventureiro | raiz | desvio máx. |
|---|---|---|---|---|
| Antes `[rom, av, raiz]` | 36,5% | 35,4% | 28,1% | 8,1 pp |
| Agora `[raiz, rom, av]` | 32,3% | 32,3% | 35,4% | 2,1 pp |

**10,4%** das combinações empatam (20 de 192). Antes 14 desses iam pro romantico; agora vão
pro raiz.

**Ressalva honesta:** a simulação assume escolha **uniforme ao acaso**. Usuário real é
auto-coerente (concentra pontuação → menos empates), então o efeito real do `TIE_ORDER` é
menor que 10,4%. A direção da correção continua certa; a magnitude é um teto.

---

## 4. Os três arquétipos e seus cardápios

O arquétipo vencedor define a identidade (emoji, cor, tagline, descrição) e os dois pools de
onde o date é sorteado: 10 jantares + 6 sobremesas. Todos vouchers reais do Tour CWB.
Cada arquétipo → 10 × 6 = **60 dates possíveis**.

### 💞 Casal Romântico — `romantico` · #E8474E
> "Clássico e apaixonado: o date de vocês é puro charme."
> Luz baixa, um bom vinho, atendimento impecável e aquele brinde.

**Jantar (10 · fine dining):** Carlo Ristorante (Contemporâneo) · Mia Trattoria (Italiano) ·
Il Barbuto (Italiano) · Cantina do Délio (Italiano) · Di Paolo (Italiano) · Romeo Cucina
(Italiano) · Katezzi Gastronomia (Contemporâneo) · Paco Cocina y Bar (Latino) · Coco Bambu
(Frutos do Mar) · Anarco (Italiano)

**Sobremesa (6 · café & chocolate):** Dengo Chocolates · Kopenhagen · Havanna Cafeteria ·
Barbarella Bakery · Coffeeterie · Koffee

### 🌎 Casal Aventureiro — `aventureiro` · #0060AD
> "Curiosos por natureza: o date de vocês tem passaporte."
> Asiático, árabe, grego, peruano… querem provar o mundo inteiro juntos.

**Jantar (10 · cozinha do mundo):** Poke to Wok (Asiático) · OX Room Steakhouse (Steakhouse) ·
Gracias! Empanadas (Argentino) · Aish Baladi (Árabe) · Icaro Greek Food (Grego) · Gyoza Bar
(Japonês) · Sushi no Rolo (Japonês) · Oishi Ramen Bar (Ramen) · Tuk-Tuk (Tailandês) · Parma
Bistrô (Internacional)

**Sobremesa (6 · gelados & mundo):** Gelato Borelli · Açaí Concept · Milk Creamery · Abacazo ·
Moncloa · American Cookies

### 🍻 Casal Raiz — `raiz` · #F09040
> "Descontraídos e sem frescura: o date de vocês é raiz."
> Hambúrguer, cerveja gelada, petisco e muita risada.

**Jantar (10 · burger & bar):** Comodoro Burguer (Burger) · Gracco Burger (Burger) · Sina
Hamburgueria (Burger) · Janela Bar (Bar & Burger) · Cão Véio (Bar & Petiscos) · Ruína Bar
(Bar) · God Save The Beer (Bar & Burger) · Sambiquira Bar (Bar & Petiscos) · Boi Tatá
(Bar & Petiscos) · Bite Me (Burger)

**Sobremesa (6 · doce de rua):** American Cookies · Dóffee Donuts · Chiquinho Sorvetes ·
Carmel's Pipocas · Operária Wafferia · Let's Eggs

---

## 5. Coerência de horário — por que a curadoria é essa

O date é **sequencial e noturno**: "jantar e depois a sobremesa, na mesma noite". Se o sorteio
caísse num "jantar 21h + sobremesa que fecha 19h", o roteiro não fecharia. Por isso **todo**
item carrega `aceitaNoite: true` — só entram vouchers cujo horário de aceite cobre sexta/sábado
à noite (conferido em `vouchers_service_times` no RDS).

```js
// quiz.js — validação que roda só em DEV, no-op em produção
if (import.meta.env?.DEV) {
  for (const [key, a] of Object.entries(ARCHETYPES))
    for (const parada of ["jantar", "sobremesa"])
      for (const place of a.date[parada])
        if (place.aceitaNoite !== true)
          throw new Error(`"${place.name}" sem aceitaNoite:true`);
}
```

**Atenção:** a validação quebra o build em **dev** se faltar a flag — mas **não** verifica se o
dado bate com o RDS. A flag é uma promessa manual: se um voucher mudar o horário na fonte, o
quiz continua achando que aceita à noite. Ao mexer nos pools, conferir `vouchers_service_times`
é obrigatório e não automatizado.

---

## 6. O date sorteado e o "segredo"

Em `Result.jsx`, ao montar, sorteia-se um jantar e uma sobremesa do pool do arquétipo,
fixados no `useState` inicial (não re-sorteia a cada render):

```js
const pick = (arr) => arr[Math.floor(Math.random() * arr.length)];
const [date] = useState(() => ({
  jantar:    pick(archetype.date.jantar),
  sobremesa: pick(archetype.date.sobremesa),
}));
```

Duas paradas — **1ª · jantar** (coral) e **2ª · sobremesa** (ciano). Cada card esconde o
benefício atrás de "descobrir o segredo 🔒"; ao clicar, revela a frase real do voucher. Fallback
genérico = `TOUR_BENEFIT`: "Peça 1 prato e ganhe outro por nossa conta."

- **Refazer:** zera `answers` → novo sorteio a cada tentativa, mesmo no mesmo arquétipo.
- **Compartilhar:** `navigator.share` (ou clipboard) com nome do arquétipo + link.
- **Imagens:** `onError` → placeholder 🍽️ se o CDN falhar.

---

## 7. Lead gate & tracking

O gate (`LeadGate.jsx`) trava o resultado e captura **nome** (mín. 2 letras), **WhatsApp**
(mín. 10 dígitos, sem máscara) e **e-mail** (opcional, regex). Ao enviar, `QuizApp.handleLead`
recalcula a pontuação e POST em `/quiz-casal/api/leads`:

```js
await submitLead({
  name, phone, email,
  quiz: "casal",
  archetype: scored.key,            // ex.: "raiz"
  archetypeName: scored.archetype.name,
  answers: { q1:"a", q2:"c", … },   // só o id da opção
  utm: { …querystring },            // rastreio de campanha
});
```

Cada CTA no resultado registra o clique via `trackClick(leadId, cta)` (`"tour"` etc.).
Best-effort com `keepalive:true`: sai mesmo se a navegação já começou, e nunca quebra a UX.

**Segurança (CLAUDE.md):** toda chamada vai pro backend em `/quiz-casal/api`, mesmo origin.
Nenhum segredo no front. O lead cai em `quiz.quiz_leads` com `quiz='casal'` — tabela
compartilhada dos quizzes, diferenciada pela coluna.

---

## 8. As perguntas cobrem todas as variedades?

Resposta curta: **as 3 vibes centrais estão bem cobertas e mapeiam limpo pros pools — mas há
gaps de eixo e uma incoerência jantar-vs-domingo que valem decisão.**

### Cobertura por arquétipo — variedade interna dos pools
| Arquétipo | Cozinhas distintas (jantar) | Concentração | Leitura |
|---|---|---|---|
| romantico | 4 | 6 de 10 italianos | Pool mais **estreito**; "fine dining" vira quase "noite italiana". |
| aventureiro | 9 | bem espalhado | Pool mais **rico**; casa com a promessa "passaporte". |
| raiz | 3 | burger + bar/petisco | Dois temas fortes; coeso, mas pouca surpresa. |

Cada opção mapeia limpo pra um pool. O gap não é de mapeamento — é de **eixos que o quiz não
pergunta**.

### Gaps e incoerências pra decidir
- **"Comemorar com estilo" é órfã.** Estilo ≠ romance; um casal raiz comemora com estilo numa
  churrascaria cara. Ficou encaixada no romantico (`romantico:2`). **Recomendação:** mexer no
  *texto* ("Comemorar a dois com capricho"), não nos pontos — split numérico desequilibra.
- **Domingo pergunta dia, resultado entrega noite.** A q4 fala de domingo (brunch, parque), mas
  o date é sempre jantar → sobremesa noturno (invariante `aceitaNoite`). Promessa torta.
- **"Docinho" é redundante.** Sinaliza querer sobremesa — mas todo date já termina em sobremesa.
  Sinal fraco, e ainda divide ponto.
- **Eixos ausentes.** Nada separa leve/saudável × indulgente, econômico × premium, ou
  vegetariano. Os 3 arquétipos são Romântico-fino / Mundo / Boteco. Decisão de produto.
- **Assimetria a observar — não urgente.** O romantico tem 6 portas de entrada contra 5 dos
  outros, mas com o `TIE_ORDER` novo ele fica em **32,3% — abaixo do raiz**. Não há desequilíbrio
  de resultado hoje; é ponto pra vigiar **se novas perguntas forem adicionadas**. (Removendo o
  "docinho" da q2 — ver acima — as portas caem pra 5 e a distribuição vai a 33,3/33,3/33,3.)

**Veredito:** pra topo de funil (divertir + capturar lead), a cobertura está **boa**. Os itens
acima são refinamento, não conserto. O único de custo zero e ganho imediato: reescrever o rótulo
da "comemorar com estilo".
