*, *::before, *::after { box-sizing: border-box; }
:root {
–orange: #F47721;
–text: #1A1D2E;
–text2: #4A5070;
–card: #F8F9FC;
–border: #D8DCE8;
–green: #1A7F4B;
–yellow: #8B6A00;
–red: #B91C1C;
–radius: 8px;
–font: -apple-system, BlinkMacSystemFont, “Segoe UI”, system-ui, sans-serif;
–mono: “SF Mono”, “Fira Code”, Menlo, Consolas, monospace;
}
body { font-family: var(–font); color: var(–text); line-height: 1.65; max-width: 780px; margin: 0 auto; padding: 0 20px 80px; }
h1 { font-size: 2rem; font-weight: 800; line-height: 1.2; margin: 0 0 .5rem; text-wrap: balance; }
h2 { font-size: 1.3rem; font-weight: 700; margin: 2.5rem 0 .75rem; line-height: 1.3; }
h3 { font-size: 1.05rem; font-weight: 700; margin: 1.75rem 0 .5rem; }
p { margin: 0 0 1rem; }
ul, ol { margin: 0 0 1rem; padding-left: 1.4rem; }
li { margin-bottom: .4rem; }
strong { font-weight: 700; }
code { font-family: var(–mono); font-size: .85em; background: #EEF0F8; padding: 1px 5px; border-radius: 3px; }
a { color: var(–orange); text-decoration: none; }
a:hover { text-decoration: underline; }
.breadcrumb { font-size: .78rem; color: var(–text2); margin-bottom: 1.5rem; }
.breadcrumb a { color: var(–text2); }
.breadcrumb span { margin: 0 .4rem; }
.meta-bar { display: flex; gap: 16px; flex-wrap: wrap; font-size: .78rem; color: var(–text2); margin-bottom: 2rem; padding-bottom: 1rem; border-bottom: 1px solid var(–border); }
.meta-bar .author { font-weight: 600; color: var(–text); }
.lead { font-size: 1.1rem; color: var(–text2); line-height: 1.65; margin-bottom: 1.5rem; }
.cta-box { background: linear-gradient(135deg,#FFF3E8,#FFF8F0); border: 1px solid rgba(244,119,33,.3); border-left: 4px solid var(–orange); border-radius: var(–radius); padding: 18px 20px; margin: 2rem 0; }
.cta-box p { margin: 0 0 .5rem; font-size: .9rem; }
.cta-btn { display: inline-block; background: var(–orange); color: #fff; padding: 10px 20px; border-radius: 6px; font-weight: 700; font-size: .88rem; text-decoration: none; margin-top: 10px; }
.cta-btn:hover { background: #D96A1E; text-decoration: none; }
.toc { background: var(–card); border: 1px solid var(–border); border-radius: var(–radius); padding: 18px 22px; margin: 2rem 0; }
.toc h2 { margin: 0 0 .75rem; font-size: 1rem; }
.toc ol { margin: 0; padding-left: 1.2rem; }
.toc li { margin-bottom: .3rem; font-size: .88rem; }
.toc a { color: var(–text2); }
.tbl-wrap { overflow-x: auto; margin: 1.5rem 0; border-radius: var(–radius); border: 1px solid var(–border); }
table { width: 100%; border-collapse: collapse; font-size: .85rem; }
th { background: #EEF0F8; padding: 9px 12px; text-align: left; font-weight: 700; font-size: .78rem; text-transform: uppercase; letter-spacing: .04em; color: var(–text2); border-bottom: 1px solid var(–border); white-space: nowrap; }
td { padding: 9px 12px; border-bottom: 1px solid var(–border); vertical-align: top; }
tbody tr:last-child td { border-bottom: none; }
tbody tr:nth-child(even) td { background: #FAFBFE; }
td.num { font-variant-numeric: tabular-nums; text-align: right; font-family: var(–mono); font-size: .82rem; }
td.hl { background: rgba(244,119,33,.06) !important; font-weight: 600; }
.ok { color: var(–green); font-weight: 700; }
.warn{ color: var(–yellow); font-weight: 700; }
.no { color: var(–red); font-weight: 700; }
.note { padding: 13px 16px; border-radius: var(–radius); margin: 1.5rem 0; display: flex; gap: 10px; align-items: flex-start; font-size: .88rem; line-height: 1.55; }
.note-warn { background: #FFFBEB; border-left: 4px solid #D97706; }
.note-info { background: #EFF6FF; border-left: 4px solid #3B82F6; }
.note-tech { background: #F0FDF4; border-left: 4px solid #16A34A; }
.note span:first-child { font-size: 1.1rem; flex-shrink: 0; }
.note p { margin: 0; }
.formula { background: #F0F4FF; border: 1px solid #C7D2FE; border-radius: var(–radius); padding: 14px 18px; margin: 1.5rem 0; font-family: var(–mono); font-size: .88rem; line-height: 1.9; }
.formula .label { font-family: var(–font); font-size: .75rem; text-transform: uppercase; letter-spacing: .06em; color: var(–text2); font-weight: 700; margin-bottom: 8px; display: block; }
.technique-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 14px; margin: 1.5rem 0; }
.technique-card { background: var(–card); border: 1px solid var(–border); border-radius: var(–radius); padding: 14px 16px; }
.technique-card.rec { border-color: var(–orange); }
.technique-name { font-weight: 700; font-size: .95rem; margin-bottom: 2px; }
.technique-tag { font-size: .7rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(–text2); margin-bottom: 10px; display: block; }
.technique-card.rec .technique-tag { color: var(–orange); }
.technique-row { display: flex; justify-content: space-between; font-size: .8rem; padding: 3px 0; border-top: 1px solid var(–border); }
.technique-row:first-of-type { border-top: none; }
.technique-row span { color: var(–text2); }
.technique-row strong { color: var(–text); }
.checklist { list-style: none; padding: 0; }
.checklist li { padding: .4rem 0 .4rem 1.8rem; position: relative; border-bottom: 1px solid var(–border); font-size: .9rem; }
.checklist li::before { content: “✓”; position: absolute; left: 0; color: var(–green); font-weight: 700; }
.checklist li:last-child { border-bottom: none; }
.faq { margin: 1.5rem 0; }
.faq-item { border-bottom: 1px solid var(–border); }
.faq-q { font-weight: 700; font-size: .95rem; padding: 14px 0 10px; cursor: pointer; display: flex; justify-content: space-between; align-items: center; }
.faq-q::after { content: “+”; font-size: 1.2rem; color: var(–orange); flex-shrink: 0; }
.faq-item.open .faq-q::after { content: “−”; }
.faq-a { display: none; font-size: .88rem; color: var(–text2); padding-bottom: 14px; line-height: 1.6; }
.faq-item.open .faq-a { display: block; }
.divider { border: none; border-top: 1px solid var(–border); margin: 2rem 0; }
.source { font-size: .72rem; color: var(–text2); margin-top: -.5rem; margin-bottom: 1rem; }
.related-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 1.5rem 0; }
.related-card { border: 1px solid var(–border); border-radius: var(–radius); padding: 12px 14px; }
.related-card a { font-weight: 600; font-size: .9rem; color: var(–text); display: block; margin-bottom: 4px; }
.related-card span { font-size: .78rem; color: var(–text2); }
@media (max-width:600px) { h1 { font-size:1.5rem; } .related-grid,.technique-grid { grid-template-columns:1fr; } }
@media (prefers-color-scheme: dark) {
:root:not([data-theme=”light”]) { –text:#E8ECF5; –text2:#9AA0BC; –card:#1C2030; –border:#2A2F45; }
body { background:#0F1117; } code { background:#22273A; } th { background:#1C2030; }
tbody tr:nth-child(even) td { background:rgba(255,255,255,.02); }
.toc,.technique-card,.related-card { background:#1C2030; }
.note-warn { background:#2D2000; } .note-info { background:#0A1628; } .note-tech { background:#0A1F12; }
.formula { background:#0D1530; border-color:#2A3A6A; }
.cta-box { background:linear-gradient(135deg,#2D1A08,#3D2410); }
}
[data-theme=”light”] body { background:#fff; }
[data-theme=”dark”] body { background:#0F1117; }
Infraestrutura para fine-tuning de LLMs: o que você realmente precisa
Revisão técnica: [Revisor]·
Publicado em agosto de 2026·
⏱ 13 min de leitura
Fine-tuning adapta um LLM pré-treinado ao seu domínio específico — linguagem jurídica, dados médicos, produtos da empresa — sem treinar do zero. Os requisitos de infraestrutura variam enormemente conforme a técnica: um LoRA em 7B cabe em uma GPU de 24 GB; um fine-tuning completo de 70B exige dezenas de GPUs. Esta página explica o que cada abordagem consome e como dimensionar a infraestrutura corretamente.
Quer dimensionar infraestrutura para fine-tuning?
Informe o modelo base, o volume de dados e a técnica pretendida. Nossa equipe calcula os requisitos e apresenta as opções de GPU dedicada.
1. O que é fine-tuning e quando usar
Fine-tuning é o processo de continuar o treinamento de um LLM pré-treinado usando um dataset menor e específico, ajustando os pesos do modelo para um domínio ou comportamento desejado. O resultado é um modelo que mantém o conhecimento geral do base model, mas performa melhor nas tarefas para as quais foi adaptado.
Quando fine-tuning faz sentido
- Domínio especializado: linguagem jurídica, terminologia médica, nomenclatura de produtos específicos que o modelo base não conhece bem.
- Tom e persona: adaptar o modelo a um estilo de comunicação específico da empresa.
- Formato de saída: ensinar o modelo a sempre responder em JSON estruturado, seguir um template de documento, etc.
- Reduzir dependência de prompt engenharia: comportamentos que hoje precisam de prompts longos e frágeis ficam internalizados no modelo.
Quando fine-tuning não é a solução certa
- Conhecimento novo e atualizado frequentemente: RAG (Retrieval-Augmented Generation) é mais adequado para documentos que mudam constantemente.
- Base de dados pequena (<100 exemplos): few-shot prompting pode ser suficiente com custo zero.
- Orçamento limitado e prazo curto: um bom prompt em um modelo de API pode ser mais rápido que configurar infra de fine-tuning.
Fine-tuning vs. RAG: as duas abordagens se complementam. Fine-tuning ensina o modelo a se comportar de uma certa forma; RAG fornece o conhecimento atualizado no momento da resposta. Para a maioria das aplicações corporativas, a combinação é mais eficaz do que escolher um ou outro.
2. Full fine-tuning, LoRA e QLoRA: diferenças de infra
A escolha da técnica é o fator que mais impacta os requisitos de infraestrutura. As três abordagens principais têm perfis de consumo muito distintos:
Para a maioria dos cenários corporativos com modelos 7B–70B: LoRA com o modelo base em BF16 é o ponto de partida recomendado. QLoRA quando a VRAM é o principal limitante (hardware limitado ou modelos muito grandes). Full fine-tuning quando o dataset é grande (>10k exemplos de alta qualidade) e a qualidade final é crítica.
3. VRAM por técnica e por modelo
A VRAM para fine-tuning é significativamente maior do que para inferência, porque além dos pesos do modelo é necessário armazenar gradientes, estados do optimizer (Adam guarda 2 estados por parâmetro) e ativações intermediárias.
| Modelo | Parâmetros | Full FT (BF16) | LoRA (BF16) | QLoRA (NF4) |
|---|---|---|---|---|
| Llama 3.2 3B | 3B | ~24 GB | ~8–12 GB | ~4–6 GB |
| Llama 3.1 8B / Gemma 2 9B | 7–9B | ~56–72 GB | ~16–24 GB | ~8–12 GB |
| Mistral 7B / Qwen 2.5 7B | 7B | ~56 GB | ~16–20 GB | ~8–10 GB |
| Llama 3.1 13B | 13B | ~96 GB | ~28–36 GB | ~14–18 GB |
| Llama 3.1 70B / Qwen 2.5 72B | 70–72B | >500 GB | ~160–180 GB | ~48–60 GB |
Estimativas baseadas em: documentação HuggingFace PEFT, artigo QLoRA (Dettmers et al., 2023), bitsandbytes e experiências relatadas pela comunidade. Full FT assume Adam com bf16 + fp32 optimizer states. LoRA assume rank 16–64, alpha 16–32. Valores reais variam com batch size, gradient checkpointing e versão do framework.
Gradient checkpointing: ativar gradient checkpointing (disponível no Hugging Face Trainer e Unsloth) reduz a VRAM de ativações em até 60–70%, com custo de ~20% mais tempo de treinamento. Recomendado sempre que a VRAM for o limitante.
4. Como calcular a VRAM necessária
VRAM ≈ (2 × params) + (2 × params) + (8 × params) + ativações
= ~12 bytes × params + ativações
Exemplo: Llama 3.1 8B full FT:
~12 × 8B = 96 GB (sem ativações) → ~110–120 GB com batch size 4
VRAM ≈ (2 × params_base) + (16 × params_lora)
Com rank=16, params_lora ≈ 0,2% de params_base:
8B base: ~16 GB + overhead adaptadores (~2–3 GB) + ativações
Total estimado: ~20–24 GB com batch size 4
VRAM ≈ (0,5 × params_base) + (16 × params_lora)
8B base em NF4: ~4 GB + adaptadores ~2 GB + ativações
Total estimado: ~8–12 GB com batch size 4
Referências: QLoRA: Efficient Finetuning of Quantized LLMs (Dettmers et al., arXiv:2305.14314); Hugging Face PEFT documentation; unsloth.ai documentation.
5. CPU, RAM e storage para fine-tuning
CPU
O fine-tuning é dominado pela GPU — a CPU cuida do carregamento de dados, pré-processamento e comunicação. Para a maioria dos cenários, 16–32 núcleos são suficientes. Configurações com múltiplas GPUs (treinamento distribuído) se beneficiam de mais núcleos para gerenciar a comunicação entre processos.
RAM do sistema
Durante o fine-tuning, o dataset de treinamento precisa ser carregado e tokenizado. A RAM necessária depende do tamanho do dataset e da estratégia de carregamento:
| Cenário | RAM recomendada | Por quê |
|---|---|---|
| Dataset pequeno (<1 GB) + 1 GPU | 32 GB | Pré-processamento + overhead OS |
| Dataset médio (1–50 GB) + 1 GPU | 64–128 GB | Carregamento em memória para velocidade |
| Dataset grande (>50 GB) + múltiplas GPUs | 256+ GB | Treinamento distribuído + DataLoader paralelo |
Storage
Fine-tuning exige storage rápido para três fins:
- Modelo base: um modelo de 70B em BF16 ocupa ~140 GB. Usar NVMe PCIe 4.0 reduz o tempo de carregamento de horas para minutos.
- Checkpoints: salvar o estado do treinamento a cada N steps. Para um modelo de 13B com LoRA, cada checkpoint pesa alguns GB; para full FT, o checkpoint inclui pesos completos.
- Dataset processado: datasets tokenizados em cache evitam reprocessamento entre runs. Um dataset de 10M tokens tokenizado pesa ~40–80 MB.
| Item | Tamanho estimado | Storage recomendado |
|---|---|---|
| Modelo 7B BF16 | ~14 GB | NVMe PCIe 4.0 |
| Modelo 70B BF16 | ~140 GB | NVMe PCIe 4.0 (400 GB+) |
| Checkpoint LoRA (rank 16) | ~500 MB–2 GB | SSD qualquer |
| Checkpoint full FT 13B | ~26 GB | NVMe — grava frequentemente |
| Dataset 100k exemplos tokenizado | ~1–5 GB | SSD (leitura intensa) |
6. Dados de treinamento: volume e formato
A qualidade dos dados de fine-tuning impacta o resultado mais do que a quantidade. Um dataset de 500 exemplos altamente curados e representativos supera 50.000 exemplos ruidosos.
Volume mínimo recomendado por técnica
| Técnica | Mínimo viável | Recomendado | Observação |
|---|---|---|---|
| QLoRA / LoRA (instruction tuning) | 200–500 exemplos | 2.000–10.000 | Alta qualidade compensa menos volume |
| LoRA (domain adaptation) | 1.000 exemplos | 10.000–100.000 | Texto do domínio + pares Q&A |
| Full fine-tuning | 10.000 exemplos | 100.000+ | Menos dados = risco de overfitting |
Formato padrão: instrução-resposta (Alpaca / ChatML)
O formato mais usado para instruction tuning é o template instrução–resposta. Cada exemplo no dataset tem:
instruction: o que o modelo deve fazerinput(opcional): contexto adicionaloutput: a resposta esperada
Frameworks modernos como Unsloth e TRL aceitam datasets nesse formato diretamente via Hugging Face Datasets ou arquivos JSONL locais.
LGPD e dados de treinamento: se o dataset contém dados pessoais de clientes (nomes, CPFs, histórico), o treinamento precisa estar em conformidade com a LGPD. Anonimização antes do fine-tuning é a prática recomendada. Dado pessoal que entra no fine-tuning pode ser reproduzido pelo modelo em produção.
7. Frameworks de fine-tuning
| Framework | Técnica | Velocidade | Complexidade | Licença |
|---|---|---|---|---|
| Unsloth | QLoRA / LoRA | Muito alta (2–5× vs. padrão) | Baixa | Apache 2.0 / AGPL |
| HF TRL + PEFT | LoRA, QLoRA, DPO, PPO | Alta | Média | Apache 2.0 |
| LLaMA-Factory | LoRA, QLoRA, full FT | Alta | Baixa (UI web) | Apache 2.0 |
| Axolotl | LoRA, QLoRA, full FT | Alta | Média (YAML config) | Apache 2.0 |
| NVIDIA NeMo | Full FT, LoRA distribuído | Alta (otimizado NVIDIA) | Alta | Apache 2.0 |
Referências: documentação oficial de cada framework, agosto de 2026. Velocidade relativa para LoRA em GPU NVIDIA com CUDA 12.x.
Recomendação para projetos corporativos novos: Unsloth para início rápido com LoRA/QLoRA em GPU única; HF TRL + PEFT para maior flexibilidade e integração com o ecossistema Hugging Face; LLaMA-Factory para times que preferem interface web sem código. NVIDIA NeMo para cenários de treinamento distribuído em múltiplas GPUs A100/H100.
8. GPUs recomendadas por cenário
| Cenário | Técnica | GPU recomendada | VRAM | Preço on-demand Adentro |
|---|---|---|---|---|
| LoRA em 7B (uso corporativo) | LoRA rank 16, BF16 | L40S 48GB | ~20–24 GB (folga para batch 8+) | R$ 8–14/h |
| QLoRA em 7B (hardware restrito) | QLoRA NF4 | RTX 4090 24GB | ~10–14 GB | R$ 4–8/h |
| LoRA em 13B | LoRA rank 16, BF16 | L40S 48GB | ~30–38 GB | R$ 8–14/h |
| QLoRA em 70B | QLoRA NF4 | 2× L40S 48GB ou A100 80GB | ~50–60 GB | R$ 10–18/h |
| Full FT em 7B | Full BF16 + Adam | A100 80GB | ~60–80 GB (grad checkpointing) | R$ 10–18/h |
| Full FT em 13B+ | Full BF16 + Adam | 2× A100 80GB ou H100 | ~100–130 GB | R$ 15–28/h |
Preços on-demand Adentro Data Center, agosto de 2026. VRAM estimada com gradient checkpointing ativo e batch size 4–8. Valores reais dependem do modelo exato, rank LoRA e versão do framework.
Fine-tuning não é um processo contínuo — é pontual.
A maioria das empresas faz fine-tuning em horas ou dias, não meses. GPU on-demand pode ser mais econômica do que contrato mensal para esse workload. Consulte nossa equipe para dimensionar o tempo e o custo total do seu projeto.
9. Estimativa de duração do treinamento
A duração do fine-tuning depende do tamanho do dataset, do modelo, da GPU e do número de épocas. Como referência geral:
| Cenário | Dataset | GPU | Duração estimada (3 épocas) |
|---|---|---|---|
| QLoRA 7B | 5.000 exemplos (~2k tokens médios) | RTX 4090 | 3–6 horas |
| LoRA 7B | 5.000 exemplos | L40S 48GB | 1–3 horas |
| LoRA 13B | 10.000 exemplos | L40S 48GB | 4–8 horas |
| QLoRA 70B | 5.000 exemplos | A100 80GB | 12–24 horas |
| Full FT 7B | 50.000 exemplos | A100 80GB | 8–16 horas |
Estimativas baseadas em relatos públicos da comunidade (Hugging Face forums, Reddit r/LocalLLaMA) e documentação Unsloth. Valores altamente dependentes do hardware real, versão de CUDA, batch size e comprimento das sequências. Faça um run de teste com 100–200 exemplos para calibrar a estimativa para o seu cenário.
Fine-tuning é workload de burst, não contínuo: ao contrário da inferência (que roda 24×7), o fine-tuning ocorre em janelas pontuais — horas ou dias. Isso significa que GPU on-demand (paga por hora) pode ser mais econômica do que um contrato dedicado mensal para esse workload específico. A infraestrutura dedicada faz mais sentido quando há ciclos frequentes de retreinamento (semanais ou mais).
10. Checklist de infraestrutura para fine-tuning
- Modelo base definido: nome, versão, número de parâmetros
- Técnica escolhida: LoRA, QLoRA ou full fine-tuning — e justificativa
- VRAM calculada para a técnica e o modelo escolhidos
- GPU selecionada com VRAM suficiente + margem de 20%
- Gradient checkpointing avaliado se VRAM for limitante
- RAM do sistema: pelo menos 2× a VRAM da GPU
- Storage NVMe para o modelo base e checkpoints
- Dataset preparado: formato instrução-resposta, limpo e revisado
- Volume de dados adequado à técnica escolhida
- Dados pessoais/sensíveis anonimizados antes do treinamento (LGPD)
- Framework de fine-tuning instalado e testado com batch pequeno
- Monitoramento de VRAM e temperatura durante o treinamento
- Estratégia de checkpoint: frequência de save e espaço disponível
- Ambiente de avaliação (eval dataset) separado do treino
- Plano de rollback: o modelo base original preservado caso o fine-tuning não atinja a qualidade esperada
Conteúdos relacionados
Perguntas frequentes
{
“@context”: “https://schema.org”,
“@graph”: [
{
“@type”: “Article”,
“headline”: “Infraestrutura para fine-tuning de LLMs: o que você realmente precisa”,
“description”: “O que você precisa de GPU, VRAM, CPU, storage e rede para fazer fine-tuning de LLMs. Tabelas por técnica (LoRA, QLoRA, full), modelo e escala.”,
“url”: “https://adentro.com.br/infraestrutura-fine-tuning/”,
“datePublished”: “2026-08-08”,
“dateModified”: “2026-08-08”,
“publisher”: { “@type”: “Organization”, “name”: “Adentro”, “url”: “https://adentro.com.br” },
“inLanguage”: “pt-BR”
},
{
“@type”: “BreadcrumbList”,
“itemListElement”: [
{“@type”:”ListItem”,”position”:1,”name”:”Adentro”,”item”:”https://adentro.com.br”},
{“@type”:”ListItem”,”position”:2,”name”:”Blog”,”item”:”https://adentro.com.br/blog/”},
{“@type”:”ListItem”,”position”:3,”name”:”Infraestrutura para IA”,”item”:”https://adentro.com.br/infraestrutura-para-inteligencia-artificial/”},
{“@type”:”ListItem”,”position”:4,”name”:”Infraestrutura para fine-tuning”,”item”:”https://adentro.com.br/infraestrutura-fine-tuning/”}
]
},
{
“@type”: “FAQPage”,
“mainEntity”: [
{
“@type”: “Question”,
“name”: “Qual GPU mínima para fazer fine-tuning de Llama 3 8B?”,
“acceptedAnswer”: { “@type”: “Answer”, “text”: “Com QLoRA (NF4): uma RTX 4090 de 24 GB é suficiente para batch size 4–8. Com LoRA em BF16: a L40S de 48 GB oferece mais VRAM e treinamento ~2× mais rápido.” }
},
{
“@type”: “Question”,
“name”: “Quantos exemplos de dados preciso para fine-tuning com LoRA?”,
“acceptedAnswer”: { “@type”: “Answer”, “text”: “Para instruction tuning com LoRA, 500–2.000 exemplos de alta qualidade geralmente produzem resultados notáveis. Qualidade é mais importante que quantidade.” }
},
{
“@type”: “Question”,
“name”: “Fine-tuning altera o modelo base original?”,
“acceptedAnswer”: { “@type”: “Answer”, “text”: “Com LoRA e QLoRA, os pesos do modelo base são congelados — os adaptadores ficam em arquivos separados que são mesclados no momento da inferência. Com full fine-tuning, uma cópia completa dos pesos é modificada.” }
}
]
}
]
}
document.querySelectorAll(‘.faq-q’).forEach(q => {
q.addEventListener(‘click’, () => q.parentElement.classList.toggle(‘open’));
});