/* Shared components & SVG illustrations for Palmilha Inteligente */ const { useEffect, useRef, useState } = React; // ============================================================ // Logo (official brand logo from manual de marca) // ============================================================ function Logo({ variant = 'dark', className = '', size = 'md', withSlogan = false }) { const srcMap = { dark: withSlogan ? 'assets/logo-color.png' : 'assets/logo-color-compact.png', light: 'assets/logo-white.png' }; const src = srcMap[variant] || srcMap.dark; const heights = { sm: 'h-7', md: 'h-9', lg: 'h-12', xl: 'h-16' }; return ( Palmilha Inteligente ); } // ============================================================ // Reveal-on-scroll wrapper // ============================================================ function Reveal({ children, delay = 0, as: As = 'div', className = '', ...rest }) { const ref = useRef(null); useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver((entries) => { entries.forEach((e) => {if (e.isIntersecting) {el.classList.add('is-in');io.unobserve(el);}}); }, { threshold: 0.12 }); io.observe(el); return () => io.disconnect(); }, []); return ( {children} ); } // ============================================================ // Section heading (eyebrow + title + lede) // ============================================================ function SectionHead({ eyebrow, title, lede, align = 'center', tone = 'light', maxLede = 'max-w-2xl' }) { const eyebrowColor = tone === 'dark' ? 'text-turq-500' : 'text-petrol'; const titleColor = tone === 'dark' ? 'text-white' : 'text-ink-900'; const ledeColor = tone === 'dark' ? 'text-white/70' : 'text-ink-500'; const alignClass = align === 'center' ? 'text-center mx-auto' : ''; return (
{eyebrow &&
{eyebrow}
}

{title}

{lede &&

{lede}

}
); } // ============================================================ // Buttons // ============================================================ function Button({ as: As = 'a', variant = 'primary', size = 'md', icon, className = '', children, ...rest }) { const base = 'inline-flex items-center justify-center gap-2 font-semibold rounded-full transition-all duration-300 will-change-transform'; const sizes = { sm: 'px-4 py-2 text-[13px]', md: 'px-5 py-3 text-[14px]', lg: 'px-7 py-4 text-[15px]' }; const variants = { primary: 'bg-grad-turq text-white shadow-[0_10px_24px_-10px_rgba(30,199,230,0.65)] hover:shadow-[0_18px_36px_-12px_rgba(30,199,230,0.75)] hover:-translate-y-0.5', petrol: 'bg-petrol text-white hover:bg-petrol-600 shadow-[0_10px_24px_-12px_rgba(10,91,126,0.55)] hover:-translate-y-0.5', accent: 'bg-accent text-white hover:bg-accent-600 shadow-[0_10px_24px_-12px_rgba(215,48,69,0.55)] hover:-translate-y-0.5', ghost: 'bg-white text-petrol border border-ink-200 hover:border-petrol/40 hover:text-petrol-800 hover:-translate-y-0.5', outline: 'bg-transparent text-petrol border border-petrol/30 hover:bg-petrol hover:text-white', soft: 'bg-turq-100 text-petrol hover:bg-turq-100/70', onDark: 'bg-white text-petrol-900 hover:bg-turq-100 hover:-translate-y-0.5', onDarkGhost: 'bg-transparent text-white border border-white/30 hover:bg-white/10' }; return ( {children} {icon && {icon}} ); } // ============================================================ // Lucide-style icons (inline SVG, no external dep) // ============================================================ const Ic = { base: { width: 22, height: 22, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 1.8, strokeLinecap: 'round', strokeLinejoin: 'round' } }; function Icon({ name, size = 22, className = '', strokeWidth = 1.8 }) { const common = { width: size, height: size, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth, strokeLinecap: 'round', strokeLinejoin: 'round', className }; switch (name) { case 'foot': return ; case 'spine': return ; case 'knee': return ; case 'walk': return ; case 'posture': return ; case 'activity': return ; case 'heart': return ; case 'shield': return ; case 'sparkle': return ; case 'balance': return ; case 'check': return ; case 'arrow': return ; case 'whatsapp': return ; case 'phone': return ; case 'mail': return ; case 'pin': return ; case 'clock': return ; case 'menu': return ; case 'x': return ; case 'plus': return ; case 'chev': return ; case 'star': return ; case 'instagram': return ; case 'facebook': return ; case 'yt': return ; case 'scan': return ; case 'layers': return ; case 'cpu': return ; case 'target': return ; case 'send': return ; default: return null; } } // ============================================================ // FOOT + INSOLE shared path (anatomical, foot-shaped insole) // ============================================================ // Mirrored, anatomically inspired left-foot insole outline const INSOLE_PATH = "M120 14 C 96 14, 78 28, 70 50 C 64 68, 60 86, 58 110 C 56 134, 54 160, 56 184 C 58 208, 60 232, 66 254 C 72 274, 88 290, 110 294 C 130 298, 156 296, 172 286 C 184 278, 188 264, 188 246 C 188 220, 186 192, 184 168 C 182 144, 180 118, 176 92 C 172 64, 162 36, 148 24 C 140 18, 130 14, 120 14 Z"; // Real plantar foot photo (assets/pe.png, 1086x1448). Two traced silhouettes: // FOOT_PX in image pixel coords (for the heat-overlay SVG, viewBox 0 0 1086 1448) // FOOT_BB normalized 0..1 (for CSS clip-path on the , objectBoundingBox). const FOOT_PX = "M 445 107 L 554 147 L 612 187 L 625 227 L 677 267 L 712 307 L 737 347 L 744 387 L 744 427 L 754 467 L 764 507 L 767 547 L 762 587 L 753 627 L 746 667 L 741 707 L 735 747 L 727 787 L 717 827 L 706 867 L 697 907 L 690 947 L 684 987 L 678 1027 L 674 1067 L 675 1107 L 673 1147 L 668 1187 L 658 1227 L 639 1267 L 602 1307 L 540 1325 L 529 1325 L 471 1307 L 433 1267 L 415 1227 L 407 1187 L 405 1147 L 406 1107 L 406 1067 L 403 1027 L 399 987 L 394 947 L 388 907 L 385 867 L 384 827 L 383 787 L 381 747 L 376 707 L 369 667 L 360 627 L 350 587 L 338 547 L 327 507 L 322 467 L 324 427 L 331 387 L 340 347 L 345 307 L 341 267 L 339 227 L 349 187 L 367 147 L 416 107 Z"; const FOOT_BB = "M 0.4098 0.0739 L 0.5101 0.1015 L 0.5635 0.1291 L 0.5755 0.1568 L 0.6234 0.1844 L 0.6556 0.2120 L 0.6786 0.2396 L 0.6851 0.2673 L 0.6851 0.2949 L 0.6943 0.3225 L 0.7035 0.3501 L 0.7063 0.3778 L 0.7017 0.4054 L 0.6934 0.4330 L 0.6869 0.4606 L 0.6823 0.4883 L 0.6768 0.5159 L 0.6694 0.5435 L 0.6602 0.5711 L 0.6501 0.5988 L 0.6418 0.6264 L 0.6354 0.6540 L 0.6298 0.6816 L 0.6243 0.7093 L 0.6206 0.7369 L 0.6215 0.7645 L 0.6197 0.7921 L 0.6151 0.8198 L 0.6059 0.8474 L 0.5884 0.8750 L 0.5543 0.9026 L 0.4972 0.9151 L 0.4871 0.9151 L 0.4337 0.9026 L 0.3987 0.8750 L 0.3821 0.8474 L 0.3748 0.8198 L 0.3729 0.7921 L 0.3738 0.7645 L 0.3738 0.7369 L 0.3711 0.7093 L 0.3674 0.6816 L 0.3628 0.6540 L 0.3573 0.6264 L 0.3545 0.5988 L 0.3536 0.5711 L 0.3527 0.5435 L 0.3508 0.5159 L 0.3462 0.4883 L 0.3398 0.4606 L 0.3315 0.4330 L 0.3223 0.4054 L 0.3112 0.3778 L 0.3011 0.3501 L 0.2965 0.3225 L 0.2983 0.2949 L 0.3048 0.2673 L 0.3131 0.2396 L 0.3177 0.2120 L 0.3140 0.1844 L 0.3122 0.1568 L 0.3214 0.1291 L 0.3379 0.1015 L 0.3831 0.0739 Z"; // Foot photo + pressure heatmap module. `id` must be unique per instance so the // clip/gradient ids don't collide. `heat` = [{x,y,rx,ry,c,o}] in image px coords. function FootPhoto({ id, heat = [], blend = 'multiply', edge = '#1EC7E6', pad = 'px-8 pt-12 pb-20', points = [] }) { return (
Mapa plantar do pé {heat.map((h, i) => ( ))} {heat.map((h, i) => ( ))} {points.map((p, i) => ( ))}
); } // ============================================================ // INSOLE SCAN — Hero visual // Top-down insole with pressure heatmap, animated scan, // pulsing pressure pads, axis grid + floating HUD readouts. // ============================================================ function InsoleScan() { return (
{/* Bezel / panel */}
{/* Top HUD strip */}
PI-Sense · LIVE
L · FOOT
{/* Bottom data bar */}
{[ { l: 'Antepé', v: '18', u: 'kPa', c: '#1EC7E6' }, { l: 'Arco', v: 'A.C', u: 'tipo', c: '#0A5B7E' }, { l: 'Calcâneo', v: '22', u: 'kPa', c: '#D73045' }]. map((c) =>
{c.l}
{c.v} {c.u}
)}
{/* Animated scan line */}
{/* Main illustration — real plantar foot photo + pressure heatmap */} {/* Floating HUD labels (DOM, above SVG) */}
); } // ============================================================ // INSOLE TECH — Technology section visual // Detailed top-down insole, dark theme, animated heatmap, // readouts, ruler ticks, and a corner "STEP COUNT" tile. // ============================================================ function InsoleTech() { return (
{/* Panel */}
{/* HUD top */}
SCAN · 001 · ATIVO
PI-Sense™ v2
{/* Scan line */}
{/* Left-side ruler */}
{[...Array(9)].map((_, i) =>
{i % 2 === 0 && {i * 30}}
)}
{/* Stats column on right */}
{[ { label: 'Suporte', value: 92, color: '#1EC7E6' }, { label: 'Conforto', value: 87, color: '#49D7EE' }, { label: 'Apoio', value: 95, color: '#1EC7E6' }]. map((s) =>
{s.label} {s.value}%
)}
{/* Foot photo + pressure heatmap (dark theme) */} {/* Bottom HUD readouts */}
{[ { l: 'Antepé', v: '18.4', u: 'kPa', c: 'text-turq-500' }, { l: 'Arco', v: 'A.C', u: 'tipo', c: 'text-turq-500' }, { l: 'Calcâneo', v: '22.1', u: 'kPa', c: 'text-accent' }]. map((c) =>
{c.l}
{c.v} {c.u}
)}
); } // Backwards-compat: FootScan alias used in Hero const FootScan = InsoleScan; function PressurePoint({ cx, cy, small, delay = '0s' }) { const r = small ? 3 : 4.5; return ( ); } function CornerBrackets() { const cls = "absolute w-5 h-5 border-turq-500/60"; return ( <>
); } function Hud({ top, left, right, bottom, label, value }) { const style = {}; if (top) style.top = top; if (left) style.left = left; if (right) style.right = right; if (bottom) style.bottom = bottom; return (
{label}
{value}
); } Object.assign(window, { Logo, Reveal, SectionHead, Button, Icon, FootScan, InsoleScan, InsoleTech, FootPhoto, PressurePoint, CornerBrackets, Hud, INSOLE_PATH, FOOT_PX, FOOT_BB });