// Componentes compartilhados — Landing v2 · Estúdio 9J // Logo, ON AIR, Wave, Kicker, Display, CTA, Container, Section // + SVG renderings (câmera, mesa de som, mesa de iluminação, workstation, microfone, control room) // + Modal de contato rápido // ───────────────────────────────────────────────────────────── // Tipografia // ───────────────────────────────────────────────────────────── const Kicker = ({ children, color, style }) =>
{children}
; const Display = ({ children, size = 80, color = T.cream, weight = 300, style }) =>
{children}
; // ───────────────────────────────────────────────────────────── // Logo — wordmark com ponto magenta no canto superior do "J" // ───────────────────────────────────────────────────────────── function Logo({ color = T.cream, accent = T.magenta, size = 22, short = false, live = false }) { const dot = size * 0.20; return (
{!short && ESTUDIO } 9J
); } // ON AIR function OnAir({ scale = 1, label = 'ON AIR', accent = T.magenta }) { return (
{label}
); } // Waveform function Wave({ bars = 56, height = 60, width = 480, color = T.cream, seed = 1, weight = 1, animate = false }) { const arr = Array.from({ length: bars }, (_, i) => { const v = Math.abs(Math.sin((i + seed) * 1.7) * 0.55 + Math.cos((i + seed) * 0.93) * 0.45); return 0.14 + v * 0.86; }); return ( {arr.map((h, i) => { const gap = width / bars; const x = i * gap + gap / 2; const bh = h * height * 0.94; return ( ); })} ); } // ───────────────────────────────────────────────────────────── // CTA primary — botão // ───────────────────────────────────────────────────────────── function CTA({ children, href = '#', accent = T.magenta, fg = T.ink, kind = 'primary', sub, style, onClick }) { const isPrimary = kind === 'primary'; return ( {e.currentTarget.style.transform = 'translateY(-1px)';if (!isPrimary) e.currentTarget.style.borderColor = T.cream;}} onMouseLeave={(e) => {e.currentTarget.style.transform = 'translateY(0)';if (!isPrimary) e.currentTarget.style.borderColor = T.hairline;}}> {isPrimary && } {children} {sub && {sub} } ); } // ───────────────────────────────────────────────────────────── // Containers // ───────────────────────────────────────────────────────────── function Container({ children, style, narrow }) { return (
{children}
); } function Section({ children, id, bg = T.night, fg = T.cream, py = 120, style }) { return (
{children}
); } // Header da seção: Kicker à esquerda + ref técnica à direita + Display + caption function SectionHead({ kicker, eyebrow, title1, title1Hi, title1Punct, caption, accent, displaySize = 56, captionRight = true }) { return ( <>
{kicker} {eyebrow && {eyebrow}}
{title1}{title1Hi}{title1Punct} {caption &&

{caption}

}
); } // ───────────────────────────────────────────────────────────── // SVG ILLUSTRATIONS — broadcast set // All in stroke-only line-art matching brand // ───────────────────────────────────────────────────────────── // Camera on tripod function IllusCamera({ size = 280, color = T.cream, accent = T.magenta }) { return ( {/* tripod */} {/* head plate */} {/* body */} {/* viewfinder */} {/* lens */} {/* top accessories */} CAM·01 {/* REC indicator */} ); } // Audio mixer console function IllusMixer({ size = 280, color = T.cream, accent = T.magenta }) { return ( {/* console body */} {/* divider */} {/* faders 8 channels */} {[0, 1, 2, 3, 4, 5, 6, 7].map((i) => { const x = 38 + i * 28; const fy = 140 + i * 37 % 26; return ( {/* knobs above */} ); })} {/* level meter */} {[0, 1, 2, 3, 4, 5, 6].map((i) => 4 ? accent : color} strokeWidth="2" opacity={0.4 + i * 0.1} /> )} MIXER · 8CH ); } // DMX lighting console function IllusLights({ size = 280, color = T.cream, accent = T.magenta }) { return ( {/* truss top */} {/* fixtures */} {[ { x: 70, color: accent }, { x: 120, color: '#8C5BFF' }, { x: 170, color: '#36E8E1' }, { x: 220, color: '#FF8C42' }]. map((f, i) => {/* cone */} )} {/* DMX console at bottom */} {[0, 1, 2, 3, 4, 5, 6, 7].map((i) => )} DMX·512 ); } // Workstation (dual monitors) function IllusWorkstation({ size = 280, color = T.cream, accent = T.magenta }) { return ( {/* left monitor — timeline */} {/* tracks */} {/* playhead */} {/* right monitor — preview with waveform */} {/* preview face */} {/* mini wave at bottom */} {Array.from({ length: 18 }).map((_, i) => { const h = 4 + Math.abs(Math.sin(i * 1.3)) * 8; return ; })} {/* keyboard / desk */} WORKSTATION · EDIT · AI POST ); } // Microphone (suspended) function IllusMic({ size = 200, color = T.cream, accent = T.magenta }) { return ( {/* arm */} {/* shock mount */} {/* mic body */} {/* grille */} {/* foam pop filter circle */} {/* xlr cable */} {/* on indicator */} ); } // Control room — schematic top-down view function IllusControlRoom({ width = 600, height = 320, color = T.cream, accent = T.magenta }) { return ( {/* room outline */} {/* curved bench */} {/* mics */} {[0, 1, 2, 3].map((i) => { const x = 156 + i * 96; const y = 200 - Math.sin(i / 3 * Math.PI) * 22; return ( M·{i + 1} ); })} {/* 3 cameras */} {[ { x: 110, y: 100, label: 'CAM·01' }, { x: 300, y: 80, label: 'CAM·02' }, { x: 490, y: 100, label: 'CAM·03' }]. map((c, i) => {c.label} {i === 1 && } )} {/* 55" reference screen */} REF · 55" {/* lighting truss above */} {[0, 1, 2, 3, 4].map((i) => )} {/* labels */} DMX TRUSS CONTROL ROOM · TOP VIEW ); } // 55" reference screen — used in personalization function IllusScreen55({ width = 600, height = 360, color = T.cream, accent = T.magenta, screenContent = null, lightColor = '#FF3E8E', bgVariant = 'wood', shelfVariant = 'books' }) { // wall background variants const wallFill = { wood: 'rgba(180, 140, 90, 0.10)', concrete: 'rgba(120, 120, 120, 0.10)', black: 'rgba(20, 8, 32, 0.85)', purple: 'rgba(60, 40, 90, 0.20)' }[bgVariant] || 'rgba(180, 140, 90, 0.10)'; const wallStrokeOpacity = bgVariant === 'black' ? 0.4 : 0.6; const isDark = bgVariant === 'black'; const fg = isDark ? T.night : color; return ( {/* wall behind */} {/* light wash from above */} {/* shelf above (left) */} {/* shelf objects */} {shelfVariant === 'books' && } {shelfVariant === 'plants' && } {shelfVariant === 'vinyl' && } {shelfVariant === 'catalog' && } {/* 55" reference screen (right side) */} {/* screen content */} {screenContent === 'logo' && 9J SEU PROGRAMA } {screenContent === 'video' && VÍDEO DE CAPA } {screenContent === 'neutral' && WALLPAPER NEUTRO } {/* TV bezel detail */} {/* bench / desk */} {/* mics on bench */} {[150, 300, 450].map((x, i) => )} {/* floor label */} PREVIEW · STAGE A ); } // ───────────────────────────────────────────────────────────── // Modal de contato rápido // ───────────────────────────────────────────────────────────── function ContactModal({ open, onClose, copy, accent, waLink }) { const [name, setName] = React.useState(''); const [wa, setWa] = React.useState(''); const [type, setType] = React.useState(copy.modal.types[0]); const m = copy.modal; if (!open) return null; const handleSubmit = (e) => { e.preventDefault(); const msg = `Oi! Sou ${name || '[sem nome]'}. Projeto: ${type}. WhatsApp: ${wa || '[não informado]'}. Vim do site.`; const url = `https://wa.me/5511936213170?text=${encodeURIComponent(msg)}`; window.open(url, '_blank'); onClose(); }; return (
e.stopPropagation()} style={{ background: T.night, border: `1px solid ${T.hairline}`, maxWidth: 520, width: '100%', padding: '44px 44px 36px', position: 'relative', borderRadius: 6 }}> · QUICK BRIEF · {m.title}

{m.lede}

setName(e.target.value)} placeholder={m.namePh} style={inputStyle} /> setWa(e.target.value)} placeholder={m.waPh} style={inputStyle} />
); } const inputStyle = { width: '100%', padding: '14px 16px', background: T.night2, border: `1px solid ${T.hairline}`, borderRadius: 4, fontFamily: T.sans, fontSize: 15, color: T.cream, outline: 'none', transition: 'border-color .15s ease' }; function Field({ label, children }) { return (
{children}
); } // ───────────────────────────────────────────────────────────── // CTA flutuante WhatsApp (compartilhado entre home e segmentos) // ───────────────────────────────────────────────────────────── function FloatingCTA({ waLink, accent }) { return ( { e.currentTarget.style.transform = 'translateY(-2px)'; e.currentTarget.style.boxShadow = `0 12px 40px ${accent}88, 0 0 0 1px ${T.ink}22`; }} onMouseLeave={(e) => { e.currentTarget.style.transform = 'translateY(0)'; e.currentTarget.style.boxShadow = `0 8px 32px ${accent}55, 0 0 0 1px ${T.ink}22`; }}> WhatsApp ); } // ───────────────────────────────────────────────────────────── // Media Slot — placeholders pra vídeo e imagem (genéricos por ora) // ───────────────────────────────────────────────────────────── function MediaSlot({ kind = 'image', // 'image' | 'video' src = '', // se preenchido, mostra a foto de verdade sob o chrome label = '', // e.g. "B-ROLL · MAIN ROOM" caption = '', // legenda discreta abaixo aspect = '16/9', // CSS aspect-ratio accent = T.magenta, duration = '02:18', // só pra video timecode = '00:00:24', showChrome = true, // mostrar barra de status acima fill = true, // se false, ratio definido pelo container style }) { const isVideo = kind === 'video'; const hasPhoto = !!src; // Background pattern: very subtle scan lines + crosshair center return (
{/* Foto real, se houver */} {hasPhoto && {label} } {/* Scan-line pattern background — só quando placeholder */} {!hasPhoto &&
} {/* Vignette (mais leve quando há foto) */}
{/* Top chrome */} {showChrome &&
{label || (isVideo ? 'B-ROLL · LIVE' : 'IMAGE · PLACEHOLDER')} {isVideo && {timecode}} {!isVideo && 4:3 · 12MP}
} {/* Center icon (escondido quando tem foto real) */} {!hasPhoto &&
{isVideo ? // Play button
{ e.currentTarget.style.transform = 'scale(1.08)'; e.currentTarget.style.borderColor = accent; e.currentTarget.style.background = 'rgba(11, 5, 21, 0.55)'; }} onMouseLeave={(e) => { e.currentTarget.style.transform = 'scale(1)'; e.currentTarget.style.borderColor = 'rgba(245, 239, 224, 0.6)'; e.currentTarget.style.background = 'rgba(11, 5, 21, 0.35)'; }}>
: // Crosshair / framing marks {/* corners */} {/* crosshair */} }
} {/* Bottom chrome — scrubber for video */} {isVideo && showChrome &&
4K · 25fps · -12dB {timecode} · {duration}
} {/* Caption tag — small marker outside on the right */} {caption &&
{caption}
}
); } Object.assign(window, { Kicker, Display, Logo, OnAir, Wave, CTA, Container, Section, SectionHead, IllusCamera, IllusMixer, IllusLights, IllusWorkstation, IllusMic, IllusControlRoom, IllusScreen55, ContactModal, Field, FloatingCTA, MediaSlot, PillarIcon, Icon }); // ───────────────────────────────────────────────────────────── // PillarIcon — pequenos ícones stroke pra Áudio / Vídeo / Iluminação // ───────────────────────────────────────────────────────────── function PillarIcon({ kind, size = 36, color, accent = '#FF3E8E' }) { const c = color || T.cream; const k = kind === 'LIT' ? 'LUZ' : kind; if (k === 'AUD') { return ( ); } if (k === 'VID') { return ( ); } // LUZ if (k === 'LUZ') { return ( ); } // PRC — workstation if (k === 'PRC') { return ( ); } // INF — building / room return ( ); } // ───────────────────────────────────────────────────────────── // Icon — biblioteca unificada de ícones stroke (servicos, segmentos, passos) // ───────────────────────────────────────────────────────────── function Icon({ kind, size = 32, color, accent = '#FF3E8E', strokeWidth = 1.5 }) { const c = color || T.cream; const common = { width: size, height: size, viewBox: '0 0 32 32', fill: 'none', stroke: c, strokeWidth, strokeLinecap: 'round', strokeLinejoin: 'round' }; switch (kind) { // ─── SERVIÇOS ─── case 'podcast': // mic on stand return ( ); case 'live': // signal tower with bars return ( ); case 'hybrid': // split screen / two participants return ( ); case 'corporate': // building / institutional return ( ); case 'branded': // mic + sparkle (brand) return ( ); case 'reels': // vertical phone with play return ( ); // ─── SEGMENTOS ─── case 'criadores': // person with play badge return ( ); case 'corporativo': // briefcase return ( ); case 'midia': // newspaper / article return ( ); case 'eventos': // calendar with star / event return ( ); // ─── COMO FUNCIONA / PASSOS ─── case 'pick': // checklist return ( ); case 'configure': // sliders return ( ); case 'record': // microphone with rec dot return ( ); case 'receive': // download / package return ( ); // fallback default: return ( ); } }