Easy-to-manage touch screens
무료 오픈소스 터치스크린으로 USB 설정 가이드, 드래그‑드롭 편집기, 로컬 제어를 제공하며 Home Assistant용으로 제작되었습니다
컬렉션의 스티커·카드를 무료로 등록해 앨범 현황을 확인하고 부족·여분을 바로 알 수 있습니다
Registre grátis cada figurinha e card das suas coleções — Álbum da Copa 2026, Legends, Gold Crumple Edition, Adrenalyn XL e mais — e saiba na hora o que falta e o que sobra. Quando quiser, transforme
라이프스타일TypeScript · CSSGitHub @fnadler온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Registre grátis cada figurinha e card das suas coleções — Álbum da Copa 2026, Legends, Gold Crumple Edition, Adrenalyn XL e mais — e saiba na hora o que falta e o que sobra. Quando quiser, transforme as repetidas em uma loja própria — os pedidos chegam no seu WhatsApp.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Handoff: Controle de Figurinhas Repetidas — Álbum Copa 2026 Overview App pessoal para registrar quais figurinhas do álbum da Copa 2026 o usuário tem e quantas repetidas de cada uma, ver o quadro completo (994 figurinhas) em uma única tela com rolagem e âncoras por grupo/seleção, filtrar por tipo (Seleções / FWC History / Coca-Cola) e por status (só repetidas / só faltantes), acompanhar totalizadores (por seleção e geral) e gerar a lista de repetidas para trocas.README 전문 보기 ↗
App pessoal para registrar quais figurinhas do álbum da Copa 2026 o usuário tem e quantas repetidas de cada uma, ver o quadro completo (994 figurinhas) em uma única tela com rolagem e âncoras por grupo/seleção, filtrar por tipo (Seleções / FWC History / Coca-Cola) e por status (só repetidas / só faltantes), acompanhar totalizadores (por seleção e geral) e gerar a lista de repetidas para trocas.
O protótipo atual guarda tudo em localStorage. O objetivo deste handoff é migrar a persistência para Supabase (Postgres + Auth) e publicar na Vercel, mantendo a interface como está.
Os arquivos deste pacote são referências de design feitas em HTML — protótipos que mostram aparência e comportamento pretendidos, não código de produção para copiar direto. A tarefa é recriar este design no ambiente do codebase de destino (aqui: um projeto novo Next.js, já que não existe codebase) usando padrões e bibliotecas estabelecidos, e ligar a persistência ao Supabase.
Figurinhas Copa 2026.dc.html é um componente de um runtime proprietário de design (template + classe de lógica). Leia-o como especificação: a estrutura de dados, os cálculos de totais, os estados de célula e os estilos inline são todos exatos e podem ser portados para React quase 1:1. Não tente rodar esse arquivo em produção.
High-fidelity. Cores, tipografia, espaçamentos, estados e interações são finais. Recriar fielmente.
O álbum tem 50 blocos / 994 figurinhas:
| Bloco | Tipo | Códigos | Qtde |
|---|---|---|---|
| FIFA World Cup History | FWC | 00, FWC1–FWC19 | 20 |
| 48 seleções, 4 por grupo A–L | TEAM | <COD>1–<COD>20 (ex. BRA1–BRA20) | 960 |
| Coca-Cola | CC | CC1–CC14 | 14 |
Grupos (inferidos da ordem da planilha original do usuário — confirmar com ele antes de considerar definitivo):
A lista completa e pronta para consumo está em album.json ({ total, blocks: [{ id, nome, tipo, grupo, codes[] }] }).
qty é quantidade TOTAL em mãosqty === 0 -> não tenho (falta)
qty === 1 -> tenho 1, colada no álbum
qty >= 2 -> colada + (qty - 1) repetidas
Derivados:
coladas = count(qty >= 1)repetidas = sum(max(qty - 1, 0))faltam = 994 - coladasRode supabase_schema.sql no SQL Editor do projeto Supabase. Ele cria:
public.stickers — catálogo imutável das 994 figurinhas (já com o seed completo).public.collection — (user_id, code, qty, updated_at), PK composta; RLS liberando apenas as próprias linhas do usuário autenticado.public.collection_totals — view com coladas/repetidas por bloco.Usar Supabase Auth com magic link (OTP por e-mail) — o app é de uso pessoal, sem senha é mais simples. Sem sessão, renderizar o quadro em modo leitura com aviso de login (ou permitir uso local e sincronizar após login — ver "Migração" abaixo).
Cada clique em uma figurinha é um upsert otimista:
await supabase.from('collection')
.upsert({ user_id: user.id, code, qty: novoQty, updated_at: new Date().toISOString() },
{ onConflict: 'user_id,code' });
qty === 0 pode deletar a linha ou gravar 0 — prefira delete para manter a tabela enxuta.select code, qty from collection where user_id = ... e montar um mapa { [code]: qty }. Não fazer 994 requests.O usuário já tem marcações em localStorage (chave copa2026-figurinhas-v1, formato { v:1, atualizado, qtd: { [code]: number } }) e possivelmente em mais de um dispositivo. Implementar:
localStorage com dados e a coleção remota estiver vazia → enviar tudo em um upsert em lote e marcar como migrado.{qtd:{...}} quanto o mapa cru) e faz upsert em lote.@supabase/supabase-js e @supabase/ssr.NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY. Nunca expor a service_role key no cliente.export const dynamic = 'force-dynamic' na página autenticada.Layout
#0a120e + dois radiais decorativos: radial-gradient(1200px 500px at 12% -10%, rgba(0,196,106,.16), transparent 60%) e radial-gradient(900px 400px at 95% 0%, rgba(255,207,77,.10), transparent 60%).max-width: 1340px, padding: 18px.padding-bottom: 64px no wrapper.Header sticky (position: sticky; top: 0; z-index: 30)
background: rgba(10,18,14,.94), backdrop-filter: blur(10px), border-bottom: 1px solid #1e3328, padding: 16px 18px 10px, colunas em flex-direction: column; gap: 12px.letter-spacing: .22em, uppercase, #ffcf4d) + título "ÁLBUM COPA 2026" (Bebas Neue 40px, line-height: .92); à direita, três cartões de totais (#10201a, borda #21382c, raio 10px, padding: 8px 14px, min-width: 96px): COLADAS n/994 (Bebas 26px #7de2ab, denominador 15px #6b8a79), REPETIDAS (#ffcf4d), FALTAM (#ff8f6b). Rótulos: 10px, 600, letter-spacing: .14em, uppercase, #7f9c8c.display: flex; gap: 10px; flex-wrap: wrap:
input[type=search], flex: 1 1 240px, fundo #0d1a14, borda #21382c, raio 9px, padding: 9px 12px, 14px; foco → borda #00c46a. Placeholder "Buscar seleção ou código (ex: BRA9)". Filtra por nome da seleção, id do bloco ou código exato.#0d1a14, borda #21382c, raio 9px, padding: 3px, gap: 2px). Chip: Barlow Condensed 600, 13px, letter-spacing: .07em, uppercase, padding: 7px 12px, raio 7px; ativo → background: #00c46a; color: #04180d; inativo → transparente, color: #89a99a.
background: #ffcf4d, color: #0a120e, raio 9px, padding: 10px 14px, Barlow Condensed 700, 14px, letter-spacing: .08em, uppercase; hover #ffe08f.color: #9fc2ae, borda #21382c, raio 9px, padding: 10px 12px, 13px; hover , borda .Faixa de legenda (abaixo do header, padding: 18px 18px 0, 12px, #7f9c8c, gap: 18px, wrap)
letter-spacing: .12em).#12211a + borda 1px dashed #37563f), colada (#00c46a), repetida (#ffcf4d).#7de2ab: "Salvo HH:MM · N figurinhas na base". Erro #ff8f6b. No app com Supabase, trocar por "Sincronizado HH:MM" / "Salvando…" / "Offline — alterações pendentes".Blocos de seleção (um por bloco, gap: 14px)
background: #0e1b15, borda 1px solid #1c2f25, raio 14px, overflow: hidden, id="bl-<blockId>".padding: 12px 16px, background: linear-gradient(90deg,#132218,#0e1b15), borda inferior #1c2f25, display: flex; gap: 12px; align-items: center; flex-wrap: wrap:
letter-spacing: .14em, uppercase, raio 6px, padding: 3px 8px; TEAM → texto #7f9c8c, borda #2a4436; FWC/CC → texto #ffcf4d, borda #5a4a15.letter-spacing: .1em, uppercase, #6b8a79."18/20 coladas" (12px 600 #7de2ab) e "7 repetidas" (12px 600 #ffcf4d).#1c2f25, preenchimento #00c46a — vira #ffcf4d em 100%.display: grid; gap: 8px; padding: 14px 16px 18px; grid-template-columns: repeat(auto-fill, minmax(46px, 1fr)).Célula da figurinha
height: 46px (configurável 34–72px), raio 8px, flex centralizado, cursor: pointer, user-select: none, Barlow Condensed 700, número em round(altura * .36)px.qty 0 → fundo #12211a, borda 1px dashed #37563f, texto #5d7a6c; qty 1 → fundo/borda #00c46a, texto #04180d; qty ≥ 2 → fundo/borda #ffcf4d, texto #3a2a00.qty ≥ 2): +n no canto superior direito (top: -6px; right: -5px), fundo #0a120e, texto/borda #ffcf4d, raio 99px, 10px/700, padding: 1px 5px.BRA9 → "9"; a primeira do bloco FWC é "00").title = "BRA9 — tenho 3 (2 repetidas) · clique para somar, clique direito inverte".+1 (ou -1 no modo Tirar); clique direito faz o inverso e dá preventDefault. Limites 0..99. transition: transform .08s ease (prever um leve scale(.94) no active).<button> com aria-label completo e navegação por setas dentro do grid.Filtros: quando Status ≠ "Tudo" (ou há busca por código), as células não correspondentes são removidas do grid e blocos que ficam sem células desaparecem. Se nada sobra: mensagem centralizada "Nenhuma figurinha com esses filtros." (padding: 60px 20px, 15px, #6b8a79). Os totalizadores sempre refletem a base completa, não o filtro.
Overlay rgba(5,9,7,.78) + blur(4px), z-index: 60, centralizado, padding: 20px. Card max-width: 620px, max-height: 84vh, fundo #0e1b15, borda #21382c, raio 16px. Cabeçalho: título Bebas 27px "Minhas repetidas" + contagem (12px #7f9c8c) + botão amarelo COPIAR LISTA (vira "Copiado!") + botão × 32×32. Corpo rolável white-space: pre-wrap, 14px, line-height: 1.7, #cfe3d7.
Formato do texto:
REPETIDAS — ÁLBUM COPA 2026
África do Sul: RSA2 (x5), RSA3 (x21), RSA4 (x23)
Coreia do Sul: KOR2, KOR3 (x3)
(sem sufixo quando há apenas 1 repetida; só blocos com repetidas aparecem; vazio → "Nenhuma repetida registrada ainda.")
Mesmo card (z-index 70). Backup: aviso "Este texto é a sua base completa (N figurinhas)…", textarea readonly 190px com o JSON, botão Copiar (navigator.clipboard + fallback select()/execCommand) e Tentar baixar arquivo (data URL). Importar: textarea editável com placeholder "Cole aqui o texto do backup...", botão verde RESTAURAR BASE; JSON inválido → alerta. Aceita {v,atualizado,qtd:{}} ou o mapa cru. Textarea: fundo #0a120e, borda #21382c, raio 10px, padding: 12px, monospace 12px, line-height: 1.5, color: #cfe3d7.
| Estado | Tipo | Notas |
|---|---|---|
qtd | Record<string, number> | fonte da verdade da coleção; ausente = 0 |
tipo | 'ALL'|'TEAM'|'FWC'|'CC' | filtro de tipo |
stat | 'ALL'|'REP'|'MISS' | REP = qty >= 2, MISS = qty === 0 |
modo | 'add'|'sub' | efeito do clique simples (essencial no celular) |
busca | string | nome/código, case-insensitive |
trocas | boolean | modal de repetidas |
backup | null|'export'|'import' | modal de backup |
importText | string | textarea de importação |
salvoEm | Date | 'erro' | null | indicador de gravação |
Na versão Supabase, acrescentar session, syncing e uma fila de upserts pendentes (para funcionar offline em banca de troca e sincronizar ao voltar a conexão).
Cores
#0a120e fundo · #0e1b15 card · #10201a cartão de total · #0d1a14 campo · #12211a célula vazia · #132218 cabeçalho do card
Bordas: #1c2f25, #1e3328, #21382c, #2a4436, #37563f (dashed), #3b5c4a (hover), #5a4a15 (FWC), #6b1f1f (CC)
Texto: #e8f2ec · #cfe3d7 · #9fc2ae · #89a99a · #7f9c8c · #6b8a79 · #5d7a6c
Acentos: #00c46a colada (configurável) · #04180d texto sobre verde · #ffcf4d repetida/destaque · #ffe08f hover · #3a2a00 texto sobre amarelo · #7de2ab positivo · #ff8f6b faltam/erro · #ff9c9c CC
Overlay: rgba(5,9,7,.78) + blur(4px); header rgba(10,18,14,.94) + blur(10px)
Tipografia (Google Fonts)
Bebas Neue — títulos e números grandes (40 / 27 / 26 / 25px).
Barlow Condensed 500/600/700 — rótulos, chips, botões, número da célula (uppercase, letter-spacing .06–.22em).
Barlow 400/500/600/700 — corpo (12/13/14px).
Monospace do sistema — textareas de backup.
Espaçamento 2 · 3 · 6 · 7 · 8 · 10 · 12 · 14 · 16 · 18 px — Raios 4 · 6 · 7 · 8 · 9 · 10 · 14 · 16 · 99px — Transição transform .08s ease
Nenhuma imagem. Sem bandeiras/escudos (só código e nome da seleção) — se o usuário quiser bandeiras depois, sugerir SVGs licenciados em public/flags/<COD>.svg, nunca desenhadas à mão.
Figurinhas Copa 2026.dc.html — protótipo completo (template + lógica): referência exata de estilos, cálculos e interações.album.json — catálogo das 994 figurinhas (50 blocos, tipo, grupo, códigos em ordem).supabase_schema.sql — DDL, RLS, view de totais e seed das 994 figurinhas.Controle_Figurinhas_Ludopedio_pdf.pdf — planilha original do usuário, fonte da numeração.Implementado: Next.js (App Router) + TypeScript, sem Tailwind (CSS em app/globals.css com os tokens deste documento), @supabase/supabase-js + @supabase/ssr, auth por magic link, sync otimista com debounce de 300ms e fila offline, migração automática do localStorage no primeiro login, e PWA (manifest + ícones gerados). Ver components/AlbumApp.tsx como orquestrador principal.
Nota de versão: o projeto foi gerado com Next.js 16, que renomeou middleware.js para proxy.js (mesmo mecanismo, arquivo/nome de export diferentes — ver proxy.ts na raiz).
npm install
npm run dev
Crie um projeto em supabase.com, rode supabase_schema.sql no SQL Editor dele, e preencha .env.local (veja .env.example):
NEXT_PUBLIC_SUPABASE_URL=...
NEXT_PUBLIC_SUPABASE_ANON_KEY=...
Em Authentication → URL Configuration no painel do Supabase, adicione http://localhost:3000 (dev) e o domínio de produção (depois do deploy) em Site URL / Redirect URLs — sem isso o magic link não funciona.
Rode também supabase_migration_shop.sql no SQL Editor (depois das outras migrações). Ela cria shops, sticker_prices, orders e a função set_order_status.
/vendas (menu da conta → "Loja e pedidos"): link da loja, loja aberta/pausada, WhatsApp que recebe os pedidos, pedido mínimo, preços por grupo (FWC / Seleções / Coca-Cola) e individuais, e a lista de pedidos./loja/<token>: catálogo público com carrinho. Só vende repetidas (qty - 1) com preço definido; o preço individual sobrepõe o do grupo.novo e o comprador o envia pelo WhatsApp (wa.me) do anunciante. Frete é combinado por lá.supabase_migration_cart_holds.sql): cada navegador tem um carrinho (cookie copa_cart); hold_cart reserva os itens atomicamente, ajustando ao disponível. O prazo conta do primeiro item e não renova a cada alteração; vencido, as figurinhas voltam a ficar disponíveis e a próxima alteração (ou "Fechar pedido", que abre a tela de confirmação) tenta reservar de novo.supabase_migration_order_reservations.sql + supabase_migration_cart_holds.sql): pedido novo reserva os itens até reserved_until; disponível na loja = repetidas − pedidos novos no prazo − carrinhos ativos de outras pessoas. Vencido o prazo sem confirmação, o pedido vira "cancelado (expirado)" — a liberação é imediata (as contas olham o prazo) e a troca de status acontece na próxima leitura (expire_orders), sem cron. Criação (place_order), edição (update_order_items) e confirmação usam trava por vendedor para não vender a mesma repetida duas vezes.SUPABASE_SERVICE_ROLE_KEY no servidor (mesma env var do link público).Acesso à loja (por usuário). Rode supabase_migration_shop_access.sql. Só quem tem uma linha ativa em shop_entitlements vê "Loja e pedidos", acessa /vendas e tem a loja pública no ar. Para habilitar alguém: Supabase → Table Editor → shop_entitlements → inserir user_id (de Authentication → Users), com expires_at opcional. Usuários não conseguem se autoconceder acesso (não há policy de escrita). Loja de quem perdeu o acesso aparece como "pausada".
Legends. Coleção à parte de 80 figurinhas (20 atletas × Lilás, Bronze, Prata, Ouro), códigos LIL1–20, BRO1–20, PRA1–20, OUR1–20 — o número é o atleta em ordem alfabética (nomes em album.json → labels). Rode supabase_migration_legends.sql (sem ela, marcar Legends falha e a loja não carrega). Aparecem no quadro, na loja e em "Minhas repetidas", mas não entram em Coladas/Faltam do álbum (ver countsTowardAlbum em lib/album.ts).
Fotos das figurinhas. Os originais ficam em /images (fora do git, ~420 MB). npm run images gera WebP otimizados em public/stickers/{thumb,large} (~48 MB, versionados) e lib/sticker-images.json. Rode de novo sempre que adicionar/trocar fotos. Figurinha sem foto mostra o número como placeholder.
.env.local nas configurações do projeto na Vercel.Criar a coleção é grátis; a loja exige assinatura mensal (cartão, com teste grátis na primeira vez).
supabase_migration_subscriptions.sql (tabelas subscriptions e stripe_events, coluna shop_entitlements.source e a função sync_subscription).STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, STRIPE_PRICE_ID. Sem elas, /assinar mostra "em breve"./assinar → Checkout do Stripe → /assinar/sucesso (sincroniza na hora) → /vendas. O webhook /api/stripe/webhook mantém tudo em dia (renovação, falha, cancelamento). O perfil abre o portal do Stripe ("Gerenciar assinatura").cancel_reason = 'assinatura'). Acessos manuais (source = 'manual') nunca são rebaixados.stripe listen --forward-to localhost:3000/api/stripe/webhook./ — landing de divulgação (route group app/(marketing), visual claro próprio, fontes Nunito/Figtree só nessa área). Estática, regenerada de hora em hora (preço lido do Stripe). Nunca redireciona; com sessão, os CTAs viram "Ir para minha coleção". Texto em content/landing.ts./colecao — o álbum (antes em /). Login e confirmação de e-mail seguem para ?next= (só caminhos internos) ou para cá./login?modo=cadastro&next=/assinar — abre na aba de cadastro e segue para a assinatura./termos, /privacidade, /cancelamento, /contato — textos em content/legal.ts (primeira versão, a revisar com advogado). Preencha COMPANY (razão social, CNPJ, endereço, e-mail, foro).lib/brand.ts + supabase_migration_reserved_slugs.sql.Cada coleção é uma linha em albums (wc2026-panini = Álbum Copa do Mundo 2026, wc2026-adrenalyn = Adrenalyn XL). O código de cada item é único na plataforma inteira: o Álbum Copa usa os códigos de sempre (BRA1, FWC3, LIL1…) e a Adrenalyn XL usa AXL1–AXL630 — por isso collection, sticker_prices, pedidos e carrinhos não precisam de chave por coleção.
supabase_migration_collections.sql (Fase 1, só aditiva). Adrenalyn XL entra inativa; cria user_albums (coleções de cada pessoa, com nome próprio; trigger em auth.users para contas novas) e shop_albums (loja por coleção: ativa, pedido mínimo, preços por grupo em jsonb); adiciona album_id em orders, cart_holds e collection_shares.data/adrenalyn-xl.csv (número, seção, seleção, tipo, posição, nome, imagem). As fotos originais ficam em data/adrenalyn-xl/cards/ (fora do git).supabase_migration_adrenalyn_limited.sql, lista em data/adrenalyn-xl-limited.csv, fonte: Collectosk): 187 cards sem número oficial, em 6 blocos (Limited Edition 90, Holo 25, Premium 12, Premium Gold 6, XXL 36, XXL Holo 18), com códigos próprios AXLLE*, AXLLH*, AXLPR*, AXLPG*, AXLXL*, AXLXH*. Mostram o nome do jogador na célula e ficam fora do total de 630. Fotos: arquivo com o próprio código em data/adrenalyn-xl/cards/ (ex: AXLLH12.jpeg).wc2026-golden-crumple, /colecao/golden-crumple-2026, supabase_migration_golden_crumple.sql): as figurinhas do Álbum Copa (FWC + 48 seleções, 980 — sem Coca-Cola e Legends) com layout dourado. Códigos com prefixo GC (GCBRA9, GC00), mostrados sem ele. Catálogo e migração gerados por node scripts/build-golden-crumple-catalog.mjs a partir do album.json. Fotos: images/golden_crumple/<CÓDIGO>.png (fora do git), recortadas dos PDFs "GOLD CRUMPLE EDITION" — o álbum completo vem das quatro partes + holográficas + Seleção Brasileira.Catalog em lib/catalog.ts (blocos, filtros, grupos de preço, etiquetas, textos). Funções por código (stickerName, describeSticker, priceFor…) usam um índice global; o que percorre blocos recebe o catálogo — nos componentes, via useCatalog() (lib/CatalogContext.tsx, padrão: Álbum Copa). O catálogo da Adrenalyn (lib/catalogs/adrenalyn-xl.json) é gerado do CSV com node scripts/build-adrenalyn-catalog.mjs.Convite para o grupo de WhatsApp dos colecionadores, por e-mail e dentro da plataforma. Tudo depende de variáveis de ambiente — sem elas, nada aparece nem é enviado:
NEXT_PUBLIC_WHATSAPP_GROUP_URL — link de convite do grupo (https://chat.whatsapp.com/...). Liga o aviso no topo da coleção (components/CommunityBanner.tsx, some depois de fechado) e o item "Comunidade no WhatsApp" no menu da conta.RESEND_API_KEY e EMAIL_FROM — envio pelo Resend (lib/email/resend.ts); o remetente precisa ser de um domínio verificado lá (padrão: GN Coleciona <[email protected]>).CRON_SECRET — protege /api/cron/convite-comunidade.Migração: supabase_migration_community_invites.sql (tabela community_invites, uma linha por conta que já recebeu). O e-mail (lib/email/communityInvite.ts; prévia em /api/comunidade/preview, só em desenvolvimento) sai uma vez por conta:
app/auth/callback);vercel.json, 10h de Brasília), para contas dos últimos 7 dias que ainda não receberam;curl -H "Authorization: Bearer $CRON_SECRET" "https://www.gncoleciona.com.br/api/cron/convite-comunidade?todos=1&limite=200" (acrescente &teste=1 para só contar; repita até pendentes chegar a 0).color: #e8f2ec#3b5c4aoverflow-x: auto, gap: 6px, padding: 2px 0 10px) com 50 botões: FWC (borda #5a4a15, fundo #1a1706, texto #ffcf4d), 48 chips "<Grupo> · <COD>" (borda #21382c, fundo #0d1a14, texto #9fc2ae, title = nome da seleção) e COCA-COLA (borda #6b1f1f, fundo #1d0b0b, texto #ff9c9c). Raio 8px, padding: 7px 10px, Barlow Condensed 600/700 13px, white-space: nowrap.
offset = headerHeight + 12; window.scrollTo({ top: elTop + scrollY - offset, behavior: 'smooth' }). Cada bloco tem scroll-margin-top: 190px. Não usar offset fixo — o header muda de altura conforme a quebra de linha.supabase_migration_collections_phase2.sql): Adrenalyn XL ativa, um link público por coleção, colecoes reservado. Rotas: /colecao abre a última coleção usada (cookie gn_colecao), /colecao/<slug> (copa-2026, adrenalyn-xl) abre uma coleção, /colecoes adiciona coleções. O nome de cada coleção fica em user_albums.collection_name (editável em Meu perfil). Fotos dos cards: npm run images (lê data/adrenalyn-xl/cards/NNN.jpeg; gera só o que falta, -- --force refaz tudo).supabase_migration_collections_phase3.sql): a loja vende várias coleções. shop_albums guarda, por coleção, se está à venda, o pedido mínimo e o preço por grupo (as colunas price_*_cents/min_order_cents de shops ficam só como legado). Carrinho por coleção (cart_holds com album_id na chave; hold_cart/place_order recebem p_album) e o pedido grava a coleção. Vitrine: abas ?colecao=<slug> quando há mais de uma à venda. Admin: seletor de coleção em Preços; "Coleções à venda" em Configurações.