// KineSuite landing — extra sections: animated hero phone, // clinics (B2B), changelog, cookie banner. const { useState: useS, useEffect: useE, useRef: useR } = React; function usePhoneWidth(max = 360, min = 260) { const [w, setW] = useS(max); useE(() => { const update = () => { const next = Math.min(max, Math.max(min, window.innerWidth - 48)); setW(next); }; update(); window.addEventListener('resize', update); return () => window.removeEventListener('resize', update); }, [max, min]); return w; } function HeroMockDisclaimer({ lang }) { const text = LANDING_COPY[lang].heroMockDisclaimer; return (

{text}

); } function HeroPhoneStack({ lang, width: widthProp, animated, children }) { const responsiveW = usePhoneWidth(); const width = widthProp != null ? Math.min(widthProp, responsiveW) : responsiveW; return (
{animated ? ( ) : ( {children} )}
); } // ───────────────────────────────────────────────────────────── // AnimatedHeroPhone — auto-cycles through 3 app screens // Catalog → Eval → Result → loop, with a crossfade and a // play/pause control. Keeps the same LandingPhone bezel. // ───────────────────────────────────────────────────────────── function AnimatedHeroPhone({ lang, width = 360 }) { const screens = ['catalog', 'eval', 'result']; const labels = { es: { catalog: 'Catálogo', eval: 'Evaluación · Berg', result: 'Resultado' }, en: { catalog: 'Library', eval: 'Assessment · Berg', result: 'Result' }, }; const [idx, setIdx] = useS(0); const [paused, setPaused] = useS(false); useE(() => { if (paused) return; const id = setInterval(() => setIdx((i) => (i + 1) % screens.length), 3400); return () => clearInterval(id); }, [paused]); const renderScreen = (kind) => { if (kind === 'eval') return ; if (kind === 'result') return ; return ; }; return (
{/* Crossfade stack: all 3 screens, the active one is opacity 1 */} {screens.map((s, i) => (
{renderScreen(s)}
))}
{/* Step indicator + play / pause */}
{screens.map((s, i) => (
{labels[lang][screens[idx]].toUpperCase()}
); } // ───────────────────────────────────────────────────────────── // ClinicsSection — B2B card, different visual treatment // ───────────────────────────────────────────────────────────── function ClinicsSection({ t, lang }) { return (
{/* Decorative arc, brand mark echoed top-right */}
{t.clinicsEyebrow}

{t.clinicsTitle}

{t.clinicsSub}

{t.clinicsEmail}
    {t.clinicsFeatures.map((f, i) => (
  • ·{String(i + 1).padStart(2,'0')}
    {f.t}
    {f.d}
  • ))}
); } // ───────────────────────────────────────────────────────────── // ChangelogSection — timeline of versions // ───────────────────────────────────────────────────────────── function ChangelogSection({ t }) { return (
{t.changelogEyebrow}

{t.changelogTitle}

{t.changelogSub}

    {t.changelogItems.map((v, i) => { const isFirst = i === 0; return (
  1. {v.version}
    {v.date}
    {isFirst && ( {t.changelogLatest} )}
    {v.title}
      {v.notes.map((n, j) => (
    • {n}
    • ))}
  2. ); })}
); } // ───────────────────────────────────────────────────────────── // CookieBanner — minimal, dismissible, persists via localStorage // ───────────────────────────────────────────────────────────── function CookieBanner({ t }) { const [visible, setVisible] = useS(false); const [details, setDetails] = useS(false); useE(() => { try { const accepted = localStorage.getItem('kinesuite-cookies'); if (!accepted) { // Slight delay so the banner doesn't fight the page mount const id = setTimeout(() => setVisible(true), 700); return () => clearTimeout(id); } } catch (e) { /* private mode: just show it */ setVisible(true); } }, []); const dismiss = (mode) => { try { localStorage.setItem('kinesuite-cookies', mode); } catch {} setVisible(false); }; if (!visible) return null; return (
{t.cookieTitle}

{t.cookieBody}

{details && (
    {t.cookieDetails.map((d, i) => (
  • {d.t} {d.required ? t.cookieRequired : t.cookieOptional}
  • ))}
)}
); } Object.assign(window, { HeroPhoneStack, HeroMockDisclaimer, AnimatedHeroPhone, ClinicsSection, ChangelogSection, CookieBanner, });