function Cases() {
  const [active, setActive] = useState(0);
  const [dragX, setDragX] = useState(0);
  const dragState = useRef({ on: false, startX: 0, startDrag: 0 });

  const isMobile = window.innerWidth <= 480;
  const cardW = isMobile
    ? Math.round(window.innerWidth * 0.88)
    : Math.min(420, Math.round(window.innerWidth * 0.82));
  const cardH = Math.round(cardW * (540 / 420));
  const cardStep = isMobile
    ? Math.round(cardW * 0.92)
    : Math.min(340, Math.round(cardW * 0.88));

  const cases = [
    {
      cliente: 'Hyper Shockk', cat: 'Suplementação esportiva · Pré-treino · D2C / Digital-first', tone: 'warm',
      photo: 'images/cases/case-01.webp',
      contexto: 'Marca nova (Vindix) entrando no mercado brasileiro de pré-treinos, dominado por dois polos: fórmulas agressivas com comunicação intimidatória e opções de baixo custo focadas em preço por dose.',
      problema: 'O briefing queria "um pré-treino leve, saboroso e acessível". Traduzindo: mais um produto que seria esmagado entre o "hardcore que performa" e o "barato que vende".',
      insight: 'Existe um espaço negligenciado: performance funcional + experiência de consumo prazerosa. Os concorrentes brigam por mais cafeína e agressividade, ignorando o que mais reprova recompra: sabor ruim e identidade que afasta parte do público.',
      decisao: 'Reposicionar como "pré-treino de experiência" (lifestyle): fórmula competitiva, sabor protagonista, marca vibrante (não agressiva) e consumo como ritual prazeroso, não obrigação. 200mg cafeína para controle, não exagero.',
      impacto: 'Potencial de criar nova subcategoria ("pré-treino lifestyle"). Expansão de público (inclui mulheres e intermediários), redução de rejeição por experiência e aumento de recompra via sabor — principal driver ignorado pelo mercado.',
    },
    {
      cliente: 'Salada Proteíca', cat: 'Alimentação saudável · Grab & Go · Proteína funcional · Varejo refrigerado', tone: 'cool',
      photo: 'images/cases/case-02.webp',
      contexto: 'SandPro, já consolidada em sanduíches proteicos refrigerados, entra em novo território: saladas prontas com alto teor proteico, em um mercado onde "salada proteica como categoria" ainda não existe.',
      problema: 'O produto nasce como "salada prática, saudável e proteica". Traduzindo: corre o risco de virar mais uma opção esquecida na geladeira ao lado de sanduíches, marmitas e iogurtes.',
      insight: 'O mercado não tem concorrência direta por um motivo simples: ninguém transformou salada em produto funcional ainda. Salada = leve / diet. Proteína = performance. Esses dois mundos ainda não foram fundidos de forma clara no varejo.',
      decisao: 'Não competir como "salada pronta". Criar e dominar a categoria: "salada proteica pronta". Proteína vira headline, produto deixa de ser "comida" e vira ferramenta de rotina, com proteína mensurável (21g / 16g) e zero fricção.',
      impacto: 'Resultado esperado: vira padrão mental ("preciso de proteína rápida = SandPro"). Criação de nova categoria (vantagem de first mover), posicionamento acima de saladas comuns e competição indireta dominada sobre sanduíche, marmita e iogurte.',
    },
    {
      cliente: 'Bloomie', cat: 'Wellness feminino · Suplementação funcional · Lifestyle / Ritual · D2C', tone: 'amber',
      photo: 'images/cases/case-03.webp',
      contexto: 'Marca nova entrando no segmento de suplementos wellness feminino, dentro da categoria ainda pouco madura de Super Greens no Brasil, dominada por produtos funcionais frios, promessas exageradas ou marcas aspiracionais inacessíveis.',
      problema: 'A proposta inicial: "suplemento natural, bonito, saudável e prático para mulheres". Traduzindo: mais um produto tentando competir em um mar de promessas de beleza, energia e desinchamento, onde ninguém acredita mais em ninguém.',
      insight: 'O problema do mercado não é falta de produto. É falta de significado. Marcas falam de benefício, consumidoras querem transformação. Produtos entregam função, mulheres buscam ritual. Gap claro entre suplemento funcional e experiência emocional de autocuidado.',
      decisao: 'Não posicionar como suplemento. Posicionar como "ritual diário de florescimento". Consumo deixa de ser "tomar algo" e vira momento pessoal, benefício deixa de ser técnico e vira sensação (leveza, energia, conexão). A marca compete com significado, não composição.',
      impacto: 'Bloomie não disputa prateleira, disputa lugar na rotina da mulher. Aumento de retenção (ritual > consumo pontual), redução de comparação técnica, criação de comunidade e posicionamento premium emocional mesmo com preço acessível.',
    },
    {
      cliente: '100% Whey', cat: 'Suplementação esportiva · Whey protein · Indulgência funcional · Lifestyle fitness', tone: 'dark',
      photo: 'images/cases/case-04.webp',
      contexto: 'Produto de whey protein no mercado fitness altamente saturado, onde a maioria das marcas compete em três frentes: preço, tabela nutricional e estética genérica agressiva.',
      problema: 'O produto tenta comunicar sabor, cremosidade e qualidade. Traduzindo: mais um whey dizendo que é gostoso, em um mercado onde todos dizem exatamente a mesma coisa.',
      insight: 'O produto não está vendendo proteína. Está vendendo experiência indulgente disfarçada de suplemento. Enquanto concorrentes brigam por gramas de proteína, pureza e performance, esse produto aponta para prazer, desejo e recompensa.',
      decisao: 'Reposicionar o whey como "sobremesa funcional". Foco em "cremoso", "chocolate" e "inconfundível", estética quente e quase gastronômica, mascote (gorila) que une força, carisma e memorabilidade. Produto que deixa de ser técnico para virar emocional e sensorial.',
      impacto: 'Produto sai da lógica de obrigação e entra na lógica de recompensa. Aumento de desejo imediato (compra por impulso), diferenciação clara em gôndola, ampliação de público e redução da fricção de consumo — "quero tomar", não "tenho que tomar".',
    },
    {
      cliente: 'Shake Proteico', cat: 'Suplementação · Shake proteico · Premium lifestyle · Nutrição funcional', tone: 'cool',
      photo: 'images/cases/case-05.webp',
      contexto: 'Produto inserido no mercado de whey/shakes proteicos com tentativa clara de fugir da estética agressiva do fitness tradicional, adotando linguagem visual mais limpa, premium e sofisticada.',
      problema: 'O produto quer comunicar: saudável, saboroso e premium. Traduzindo: tentando equilibrar tudo e corre o risco de não dominar nada. No mercado atual, ou você é performance, ou é indulgência, ou é lifestyle. Ficar no meio apaga a marca.',
      insight: 'O produto não é um whey tradicional. Mas também não é sobremesa fitness explícita. Está mais próximo de um território pouco explorado: nutrição sofisticada. Um espaço onde saúde não é agressiva, prazer não é exagerado e estética não é gritante.',
      decisao: 'Reposicionar como "shake proteico premium de rotina". Visual minimalista como confiança (não hype), cores neutras + dourado como luxo acessível, foco em textura cremosa como prazer controlado. O produto vira "café da manhã premium" — escolha racional com estética emocional.',
      impacto: 'Produto sai da guerra de performance e entra na lógica de consumo aspiracional diário. Aumento de percepção de valor, atração de público mais maduro, diferenciação em gôndola saturada de preto/vermelho agressivo e redução de rejeição por imagem "fitness pesada".',
    },
    {
      cliente: 'Creatina', cat: 'Suplementação esportiva · Creatina · Performance baseada em evidência · D2C / Digital-first', tone: 'warm',
      photo: 'images/cases/case-06.webp',
      contexto: 'Mercado brasileiro de creatina altamente competitivo, com dezenas de marcas disputando atenção em cenário fragmentado, onde não existe líder dominante e o consumidor está cada vez mais informado e criterioso.',
      problema: 'O mercado está saturado de creatinas. Todo mundo vende exatamente o mesmo produto e diferenciação virou maquiagem de marca. Premium: caro demais. Barato: gera desconfiança. Intermediário: irrelevante.',
      insight: 'O jogo não é sobre creatina. É sobre confiança em um mercado desacreditado. O consumidor já entendeu que creatina funciona, mas nem toda creatina é confiável e nem todo preço faz sentido. Gap claro: qualidade técnica real + transparência + preço justo.',
      decisao: 'Não competir como suplemento. Posicionar como "a escolha racional do praticante sério". Produto simples como vantagem, fórmula pura como argumento central, comunicação técnica sem hype. A marca vira referência de honestidade e atalho mental para quem pesquisa.',
      impacto: 'Não disputa atenção, disputa credibilidade. Alta conversão em público informado, forte retenção (confiança gera recorrência) e potencial de advocacy. Se acertar, abre caminho para linha completa — a proposta central é: "eu não sou enganado".',
    },
    {
      cliente: 'Colágeno em Gummy', cat: 'Nutricosméticos · Colágeno · Wellness feminino · Autocuidado diário', tone: 'amber',
      photo: 'images/cases/case-07.webp',
      contexto: 'Mercado de colágeno e nutricosméticos femininos em expansão acelerada, com crescimento puxado por autocuidado, skincare e suplementação "de dentro pra fora", especialmente no formato gummies.',
      problema: 'O mercado está cheio de promessas de beleza. Todo produto promete pele melhor — e poucos conseguem provar ou sustentar isso. Excesso de marketing, baixa credibilidade, fórmulas genéricas e abandono por falta de resultado.',
      insight: 'O problema não é falta de desejo. É falta de confiança + consistência de uso. Beleza exige rotina, rotina exige prazer, prazer exige experiência — e a maioria dos produtos falha exatamente nisso.',
      decisao: 'Não posicionar como colágeno. Posicionar como "ritual diário de autocuidado que funciona". Verisol vira protagonista (prova científica), gummy vira ferramenta (adesão), sabor vira gatilho (consistência). Produto deixa de ser suplemento e vira hábito diário emocional + funcional.',
      impacto: 'Aumento de adesão (principal gargalo da categoria), redução de ceticismo via ciência e clareza, maior retenção (ritual diário > uso ocasional) e posicionamento premium acessível. Resultado: não vende "colágeno" — vende constância que gera resultado.',
    },
  ];

  const total = cases.length;
  const prev = () => setActive((active - 1 + total) % total);
  const next = () => setActive((active + 1) % total);

  // Wheel navigate
  const stageRef = useRef(null);
  useEffect(() => {
    const el = stageRef.current;
    if (!el) return;
    let lock = false;
    const onWheel = (e) => {
      // Only horizontal swipe — never intercept vertical page scroll
      if (Math.abs(e.deltaX) < 30) return;
      if (Math.abs(e.deltaY) > Math.abs(e.deltaX)) return;
      if (lock) return;
      lock = true;
      if (e.deltaX > 0) next(); else prev();
      setTimeout(() => lock = false, 550);
    };
    el.addEventListener('wheel', onWheel, { passive: true });
    return () => el.removeEventListener('wheel', onWheel);
  }, [active]);

  // Drag
  const onDown = (e) => {
    dragState.current.on = true;
    dragState.current.startX = (e.touches?.[0]?.clientX) ?? e.clientX;
    dragState.current.startDrag = dragX;
  };
  const onMove = (e) => {
    if (!dragState.current.on) return;
    const x = (e.touches?.[0]?.clientX) ?? e.clientX;
    setDragX(dragState.current.startDrag + (x - dragState.current.startX));
  };
  const onUp = () => {
    if (!dragState.current.on) return;
    dragState.current.on = false;
    if (dragX > 80) prev();
    else if (dragX < -80) next();
    setDragX(0);
  };
  useEffect(() => {
    const up = () => onUp();
    window.addEventListener('mouseup', up);
    window.addEventListener('touchend', up);
    window.addEventListener('mousemove', onMove);
    window.addEventListener('touchmove', onMove);
    return () => {
      window.removeEventListener('mouseup', up);
      window.removeEventListener('touchend', up);
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('touchmove', onMove);
    };
  }, [dragX]);

  const c = cases[active];

  return (
    <Section id="cases" style={{ borderTop: '1px solid var(--line-soft)', position: 'relative', clipPath: 'inset(0)' }}>
      {/* tech grid overlay */}
      <div aria-hidden style={{
        position: 'absolute', inset: 0,
        backgroundImage: 'linear-gradient(to right, oklch(0.30 0.006 85 / 0.25) 1px, transparent 1px), linear-gradient(to bottom, oklch(0.30 0.006 85 / 0.25) 1px, transparent 1px)',
        backgroundSize: '80px 80px',
        maskImage: 'radial-gradient(ellipse 60% 60% at 50% 50%, black 20%, transparent 70%)',
        pointerEvents: 'none',
      }} />

      <div className="container" style={{ position: 'relative' }}>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 2fr', gap: 60, marginBottom: 40 }} className="cases-head">
          <Label n="06">Cases</Label>
          <MaskLines
            tag="h2"
            className="serif"
            style={{ fontSize: 'clamp(38px, 5.5vw, 80px)', lineHeight: 0.98, letterSpacing: '-0.03em' }}
            lines={[
              <>Não é portfólio.</>,
              <>É <em className="italic">raciocínio aplicado.</em></>,
            ]}
          />
        </div>

        {/* HUD */}
        <div className="cases-hud" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '16px 0', borderTop: '1px solid var(--line-soft)', borderBottom: '1px solid var(--line-soft)', marginBottom: 40 }}>
          <div className="mono" style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
            <span style={{ width: 6, height: 6, borderRadius: 999, background: 'var(--accent)', boxShadow: '0 0 12px var(--accent)' }} />
            Case ativo · {String(active + 1).padStart(2, '0')} / {String(total).padStart(2, '0')}
          </div>
          <div className="mono hud-hint" style={{ color: 'var(--fg-mute)' }}>arraste · scroll · ←/→</div>
          <div className="mono hud-hint-mobile" style={{ color: 'var(--fg-mute)', display: 'none' }}>arraste ←/→</div>
          <div style={{ display: 'flex', gap: 10 }}>
            <CarouselBtn dir="prev" onClick={prev} />
            <CarouselBtn dir="next" onClick={next} />
          </div>
        </div>
      </div>

      {/* 3D stage */}
      <div ref={stageRef}
        onMouseDown={onDown} onTouchStart={onDown}
        style={{
          perspective: 2000,
          perspectiveOrigin: '50% 55%',
          padding: '60px 0 40px',
          cursor: dragState.current.on ? 'grabbing' : 'grab',
          position: 'relative',
        }}>
        <div style={{
          position: 'relative',
          width: '100%',
          height: cardH + 20,
          transformStyle: 'preserve-3d',
        }}>
          {cases.map((cs, i) => {
            const offset = i - active;
            const wrapped = offset > total / 2 ? offset - total : offset < -total / 2 ? offset + total : offset;
            return <CarouselCard key={i} cs={cs} idx={i} offset={wrapped} active={i === active} onClick={() => setActive(i)} dragX={dragX} cardW={cardW} cardH={cardH} cardStep={cardStep} />;
          })}
        </div>

        {/* bullets */}
        <div style={{ display: 'flex', justifyContent: 'center', gap: 6, marginTop: 32 }}>
          {cases.map((_, i) => (
            <button key={i} onClick={() => setActive(i)} style={{
              width: i === active ? 32 : 8, height: 2,
              background: i === active ? 'var(--accent)' : 'var(--line)',
              transition: 'all .5s cubic-bezier(.2,.7,.2,1)',
            }} />
          ))}
        </div>
      </div>

      {/* Active case detail */}
      <div className="container" style={{ marginTop: 60 }}>
        <div key={active} style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 40, animation: 'caseFade .6s ease both' }} className="case-body">
          <CaseRow label="Contexto" text={c.contexto} />
          <CaseRow label="Problema" text={c.problema} />
          <CaseRow label="Insight estratégico" text={c.insight} accent />
          <CaseRow label="Decisão tomada" text={c.decisao} />
          <CaseRow label="Impacto" text={c.impacto} />
          <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
            <div className="mono" style={{ color: 'var(--fg-mute)' }}>Segmento</div>
            <div className="serif" style={{ fontSize: 26, letterSpacing: '-0.015em' }}>{c.cat}</div>
            <div className="mono" style={{ marginTop: 12, color: 'var(--accent)' }}>Φ · case {String(active + 1).padStart(2, '0')}</div>
          </div>
        </div>
      </div>

      <style>{`
        @keyframes caseFade { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
        @media (max-width: 820px) {
          .cases-head { grid-template-columns: 1fr !important; gap: 20px !important; }
          .case-body { grid-template-columns: 1fr !important; gap: 24px !important; }
        }
        @media (max-width: 480px) {
          .hud-hint { display: none !important; }
          .hud-hint-mobile { display: block !important; }
          .cases-hud { gap: 10px !important; }
        }
      `}</style>
    </Section>
  );
}

function CarouselCard({ cs, idx, offset, active, onClick, dragX, cardW = 420, cardH = 540, cardStep = 340 }) {
  const absO = Math.abs(offset);
  const translateX = offset * cardStep + (active ? dragX : 0);
  const rotateY = offset * -28;
  const scale = active ? 1 : 1 - absO * 0.1;
  const opacity = absO > 2.2 ? 0 : 1 - absO * 0.28;
  const z = -absO * 180;

  const [tilt, setTilt] = useState({ x: 0, y: 0 });
  const [photoErr, setPhotoErr] = useState(false);

  const onMouse = (e) => {
    if (!active) return;
    const r = e.currentTarget.getBoundingClientRect();
    const tx = ((e.clientY - r.top) / r.height - 0.5) * -8;
    const ty = ((e.clientX - r.left) / r.width - 0.5) * 8;
    setTilt({ x: tx, y: ty });
  };
  const onLeave = () => setTilt({ x: 0, y: 0 });

  const tones = {
    warm: 'linear-gradient(135deg, oklch(0.32 0.05 60), oklch(0.22 0.02 50))',
    cool: 'linear-gradient(135deg, oklch(0.28 0.04 220), oklch(0.18 0.02 230))',
    amber: 'linear-gradient(135deg, oklch(0.72 0.13 70), oklch(0.48 0.12 55))',
    dark: 'linear-gradient(135deg, oklch(0.24 0.01 85), oklch(0.14 0.005 85))',
  };

  const showPhoto = cs.photo && !photoErr;

  return (
    <div
      onClick={onClick}
      onMouseMove={onMouse}
      onMouseLeave={onLeave}
      style={{
        position: 'absolute',
        left: '50%', top: '50%',
        width: cardW, height: cardH, marginLeft: -cardW/2, marginTop: -cardH/2,
        transform: `translate3d(${translateX}px, 0, ${z}px) rotateY(${rotateY + tilt.y}deg) rotateX(${tilt.x}deg) scale(${scale})`,
        transformStyle: 'preserve-3d',
        transition: 'transform .7s cubic-bezier(.2,.7,.2,1), opacity .6s',
        opacity,
        cursor: active ? 'default' : 'pointer',
        zIndex: 100 - absO,
      }}>
      <div style={{
        position: 'relative', width: '100%', height: '100%',
        borderRadius: 2,
        overflow: 'hidden',
        background: tones[cs.tone] || tones.dark,
        border: '1px solid oklch(0.5 0.02 70 / 0.3)',
        boxShadow: active
          ? '0 40px 80px -20px oklch(0 0 0 / 0.6), 0 0 0 1px oklch(0.78 0.13 70 / 0.3), 0 0 60px oklch(0.78 0.13 70 / 0.15)'
          : '0 20px 40px -20px oklch(0 0 0 / 0.5)',
      }}>

        {/* Photo layer */}
        {showPhoto && (
          <img
            src={cs.photo}
            alt=""
            onError={() => setPhotoErr(true)}
            style={{
              position: 'absolute', inset: 0,
              width: '100%', height: '100%',
              objectFit: 'cover', zIndex: 0,
            }}
          />
        )}

        {/* Scrim — stronger over photos */}
        <div style={{
          position: 'absolute', inset: 0, zIndex: 1,
          background: showPhoto
            ? 'linear-gradient(180deg, oklch(0 0 0 / 0.28) 0%, oklch(0 0 0 / 0.48) 55%, oklch(0 0 0 / 0.75) 100%)'
            : 'repeating-linear-gradient(0deg, transparent 0 3px, oklch(0 0 0 / 0.2) 3px 4px)',
          mixBlendMode: showPhoto ? 'normal' : 'multiply',
          pointerEvents: 'none',
        }} />

        {/* corner brackets */}
        {[['tl',20,20],['tr',null,20],['bl',20,null],['br',null,null]].map(([k,l,t]) => (
          <span key={k} style={{
            position: 'absolute', zIndex: 2,
            left: l != null ? l : 'auto', right: l == null ? 20 : 'auto',
            top: t != null ? t : 'auto', bottom: t == null ? 20 : 'auto',
            width: 14, height: 14,
            borderTop: t != null ? '1.5px solid oklch(0.96 0.008 85 / 0.6)' : 'none',
            borderBottom: t == null ? '1.5px solid oklch(0.96 0.008 85 / 0.6)' : 'none',
            borderLeft: l != null ? '1.5px solid oklch(0.96 0.008 85 / 0.6)' : 'none',
            borderRight: l == null ? '1.5px solid oklch(0.96 0.008 85 / 0.6)' : 'none',
          }} />
        ))}

        {/* content */}
        <div style={{ position: 'absolute', inset: 0, zIndex: 2, padding: '32px', display: 'flex', flexDirection: 'column', justifyContent: 'space-between', color: 'oklch(0.96 0.008 85)' }}>
          <div>
            <div className="mono" style={{ color: 'oklch(0.96 0.008 85 / 0.7)', display: 'flex', gap: 10, alignItems: 'center' }}>
              <span style={{ width: 6, height: 6, borderRadius: 999, background: 'var(--accent)', boxShadow: '0 0 10px var(--accent)' }} />
              Case Φ · {String(idx + 1).padStart(2, '0')}
            </div>
            <div className="mono" style={{ marginTop: 14, color: 'oklch(0.96 0.008 85 / 0.55)' }}>{cs.cat}</div>
          </div>
          <div>
            <h3 className="serif" style={{ fontSize: 'clamp(28px, 12vw, 52px)', letterSpacing: '-0.025em', lineHeight: 0.95 }}>{cs.cliente}</h3>
            <div style={{ height: 1, background: 'oklch(0.96 0.008 85 / 0.3)', margin: '22px 0' }} />
            <p style={{ fontSize: 13.5, lineHeight: 1.5, color: 'oklch(0.96 0.008 85 / 0.78)' }}>
              {cs.insight}
            </p>
          </div>
        </div>

        {/* spotlight on hover */}
        {active && (
          <div style={{
            position: 'absolute', inset: 0, zIndex: 3,
            background: `radial-gradient(400px at ${50 + tilt.y * 4}% ${50 + tilt.x * -4}%, oklch(1 0 0 / 0.08), transparent 50%)`,
            pointerEvents: 'none',
            transition: 'background .3s',
          }} />
        )}
      </div>
    </div>
  );
}

function CarouselBtn({ dir, onClick }) {
  return (
    <button onClick={onClick} style={{
      width: 40, height: 40, borderRadius: 999,
      border: '1px solid var(--line)',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      color: 'var(--fg)', transition: 'all .3s',
    }}
      onMouseEnter={e => { e.currentTarget.style.background = 'var(--accent)'; e.currentTarget.style.color = 'var(--bg)'; e.currentTarget.style.borderColor = 'var(--accent)'; }}
      onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.color = 'var(--fg)'; e.currentTarget.style.borderColor = 'var(--line)'; }}
    >
      <svg width="14" height="14" viewBox="0 0 14 14" fill="none" style={{ transform: dir === 'next' ? 'none' : 'rotate(180deg)' }}>
        <path d="M3 7H11M11 7L7 3M11 7L7 11" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
      </svg>
    </button>
  );
}

function CaseRow({ label, text, accent }) {
  return (
    <div>
      <div className="mono" style={{ marginBottom: 10, color: accent ? 'var(--accent)' : 'var(--fg-mute)' }}>
        {accent ? '→ ' : ''}{label}
      </div>
      <p className={accent ? 'serif' : ''} style={{
        fontSize: accent ? 22 : 15,
        lineHeight: 1.5,
        letterSpacing: accent ? '-0.015em' : '-0.005em',
        color: accent ? 'var(--fg)' : 'var(--fg-dim)',
      }}>{text}</p>
    </div>
  );
}

window.Cases = Cases;
