{"id":3316,"date":"2026-01-15T08:54:13","date_gmt":"2026-01-15T08:54:13","guid":{"rendered":"https:\/\/test.innovacore.group\/?post_type=portfolio&#038;p=3316"},"modified":"2026-01-15T12:06:37","modified_gmt":"2026-01-15T12:06:37","slug":"to-travel-agency","status":"publish","type":"portfolio","link":"https:\/\/test.innovacore.group\/zh-hans\/portfolio\/to-travel-agency\/","title":{"rendered":"to-travel.agency &#8211; Tech Power for Travel Pros"},"content":{"rendered":"\n<p>to-travel.agency is a next-generation tour operator acting as a direct bridge between travel planners, travelers, and local incoming agencies (DMCs). They provide a turnkey technological platform featuring advanced dynamic packaging, democratizing access to enterprise-grade tools. This ecosystem empowers small to mid-sized travel actors to compete with major players by optimizing local expertise and customer service at an affordable cost., with ROI guarantee<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Big-player tech and dynamic packaging for  small &#038; medium agencies independent travel pros.<\/p>\n","protected":false},"featured_media":2369,"parent":0,"template":"","meta":{"inline_featured_image":false,"company_name":"to-travel.agency - Tech Power for Travel Pros","summary":"Tech Power for <span class=\"ic-nycd\">Travel Pros<\/span>","mission":"<p>To democratize travel technology. Their mission is to equip independent planners and small agencies with the dynamic packaging and operational tools needed to compete with industry giants, ensuring that authentic local expertise remains the core value of the travel experience.<\/p>\n","impact":"<p>to-travel.agency is leveling the playing field in tourism. By giving the \"long tail\" of the market access to sophisticated dynamic packaging, they enable independent experts to reclaim market share from standardized OTAs, fostering a more diverse, expert-led, and personalized travel economy.<\/p>\n","company_logo":"https:\/\/test.innovacore.group\/wp-content\/uploads\/to-travel-logo-innov-innovacore-optimized.jpg","website_url":"","linkedin_url":"","location":"Miami, FL, USA","founded_year":"2022","key_numbers":{"item-0":{"metric_label":"Unified platform","metric_value":"1"},"item-1":{"metric_label":"% annual growth rate","metric_value":"40"},"item-2":{"metric_label":"% EBITDA expected","metric_value":"30"}},"hiring_status":"stealth","sector":"other","company_type":"startup","audience":["b2b_a_b2c","b2b2c"],"geo_scope":["global"],"business_model":["marketplace","services","subscription"],"funding_stage":"pre_seed","partnership_type":"creation","logo_animation":"<div id=\"totravel-particle-root\">\r\n    <canvas id=\"totravelCanvas\"><\/canvas>\r\n<\/div>\r\n\r\n<style>\r\n    #totravel-particle-root {\r\n        width: 100%;\r\n        height: 300px; \/* Hauteur fixe respect\u00e9e *\/\r\n        display: flex;\r\n        justify-content: center;\r\n        align-items: center;\r\n        background: transparent;\r\n        overflow: hidden;\r\n        cursor: crosshair;\r\n    }\r\n<\/style>\r\n\r\n<script>\r\n(function() {\r\n    const canvas = document.getElementById('totravelCanvas');\r\n    const ctx = canvas.getContext('2d', { willReadFrequently: true });\r\n    const root = document.getElementById('totravel-particle-root');\r\n    \r\n    let width, height;\r\n    let particles = [];\r\n    let mouse = { x: -1000, y: -1000 };\r\n    const particleColor = '#f5f5f5'; \/\/ Gris clair Innovacore\r\n    let time = 0;\r\n\r\n    function init() {\r\n        width = root.offsetWidth;\r\n        height = 300;\r\n        \r\n        const dpr = window.devicePixelRatio || 1;\r\n        canvas.width = width * dpr;\r\n        canvas.height = height * dpr;\r\n        canvas.style.width = width + 'px';\r\n        canvas.style.height = height + 'px';\r\n        ctx.scale(dpr, dpr);\r\n\r\n        createLogoParticles();\r\n    }\r\n\r\n    function createLogoParticles() {\r\n        particles = [];\r\n        const cx = width \/ 2;\r\n        const cy = height \/ 2;\r\n        const isMobile = width < 600;\r\n\r\n        \/\/ --- 1. CONFIGURATION ---\r\n        ctx.fillStyle = 'white';\r\n        ctx.strokeStyle = 'white';\r\n\r\n        \/\/ Dimensions relatives\r\n        const iconSize = isMobile ? 50 : 80;   \/\/ Taille du globe\r\n        const fontSize = isMobile ? 24 : 45;   \/\/ Taille du texte principal\r\n        const fontSizeSub = isMobile ? 12 : 18; \/\/ Taille tagline\r\n        const gap = isMobile ? 10 : 20;        \/\/ Espace Icone <-> Texte\r\n\r\n        \/\/ Police de base\r\n        const fontNormal = `normal ${fontSize}px Arial, Helvetica, sans-serif`;\r\n        const fontBold = `bold ${fontSize}px Arial, Helvetica, sans-serif`;\r\n        const fontSub = `normal ${fontSizeSub}px Arial, Helvetica, sans-serif`;\r\n\r\n        \/\/ Calcul des largeurs de texte pour l'alignement\r\n        ctx.font = fontNormal;\r\n        const wTo = ctx.measureText('to-').width;\r\n        const wAgency = ctx.measureText('.agency').width;\r\n        \r\n        ctx.font = fontBold;\r\n        const wTravel = ctx.measureText('travel').width;\r\n        \r\n        const totalTextWidth = wTo + wTravel + wAgency;\r\n        \r\n        \/\/ Largeur totale approximative (Globe + Gap + Texte)\r\n        \/\/ Le globe est un peu en fond, on le compte partiellement\r\n        const totalContentWidth = (iconSize * 0.8) + gap + totalTextWidth;\r\n        const startX = cx - (totalContentWidth \/ 2);\r\n\r\n\r\n        \/\/ --- 2. DESSIN DU GLOBE (Mappemonde Stylis\u00e9e) ---\r\n        const ix = startX + (iconSize \/ 2);\r\n        const iy = cy; \/\/ Centr\u00e9 verticalement avec le texte\r\n        \r\n        \/\/ Cercle ext\u00e9rieur (fin)\r\n        ctx.lineWidth = 2;\r\n        ctx.beginPath();\r\n        ctx.arc(ix, iy, iconSize \/ 2, 0, Math.PI * 2);\r\n        ctx.stroke();\r\n\r\n        \/\/ Continents (Formes simplifi\u00e9es pour sugg\u00e9rer la carte)\r\n        ctx.fillStyle = 'white';\r\n        ctx.beginPath();\r\n        \r\n        \/\/ Am\u00e9rique du Nord (Blob haut gauche)\r\n        ctx.moveTo(ix - iconSize*0.2, iy - iconSize*0.3);\r\n        ctx.quadraticCurveTo(ix - iconSize*0.4, iy - iconSize*0.1, ix - iconSize*0.3, iy + iconSize*0.1);\r\n        ctx.lineTo(ix - iconSize*0.1, iy);\r\n        \r\n        \/\/ Am\u00e9rique du Sud (Blob bas gauche)\r\n        ctx.moveTo(ix - iconSize*0.25, iy + iconSize*0.15);\r\n        ctx.quadraticCurveTo(ix - iconSize*0.15, iy + iconSize*0.35, ix - iconSize*0.05, iy + iconSize*0.1);\r\n        \r\n        \/\/ Europe\/Afrique (Blob droit)\r\n        ctx.moveTo(ix + iconSize*0.1, iy - iconSize*0.25);\r\n        ctx.quadraticCurveTo(ix + iconSize*0.3, iy - iconSize*0.1, ix + iconSize*0.2, iy + iconSize*0.2);\r\n        ctx.quadraticCurveTo(ix + iconSize*0.1, iy + iconSize*0.3, ix, iy + iconSize*0.1);\r\n\r\n        ctx.fill();\r\n\r\n\r\n        \/\/ --- 3. DESSIN DU TEXTE ---\r\n        \/\/ On d\u00e9cale le texte pour qu'il morde un peu sur le globe ou soit juste \u00e0 c\u00f4t\u00e9\r\n        const textStartX = startX + (iconSize * 0.8) + gap;\r\n        const textY = cy; \/\/ Ligne de base centr\u00e9e\r\n        \r\n        ctx.textAlign = 'left';\r\n        ctx.textBaseline = 'middle';\r\n\r\n        \/\/ \"to-\"\r\n        ctx.font = fontNormal;\r\n        ctx.fillText('to-', textStartX, textY);\r\n\r\n        \/\/ \"travel\" (Gras)\r\n        ctx.font = fontBold;\r\n        ctx.fillText('travel', textStartX + wTo, textY);\r\n\r\n        \/\/ \".agency\"\r\n        ctx.font = fontNormal;\r\n        ctx.fillText('.agency', textStartX + wTo + wTravel, textY);\r\n\r\n        \/\/ Tagline \"travel agent helper\"\r\n        \/\/ Align\u00e9e \u00e0 droite ou centr\u00e9e sous le texte principal\r\n        ctx.font = fontSub;\r\n        ctx.textAlign = 'right'; \/\/ Alignement \u00e0 droite pour faire joli sous \".agency\"\r\n        ctx.fillText('travel agent helper', textStartX + totalTextWidth, textY + fontSize * 0.8);\r\n\r\n\r\n        \/\/ --- 4. SCAN DES PIXELS ---\r\n        const density = 4;\r\n        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;\r\n        const dataWidth = canvas.width; \r\n\r\n        for (let y = 0; y < canvas.height; y += density) {\r\n            for (let x = 0; x < canvas.width; x += density) {\r\n                if (imageData[(y * dataWidth + x) * 4 + 3] > 128) {\r\n                    const dpr = window.devicePixelRatio || 1;\r\n                    particles.push({\r\n                        x: x \/ dpr,\r\n                        y: y \/ dpr,\r\n                        originX: x \/ dpr,\r\n                        originY: y \/ dpr,\r\n                        vx: 0, \r\n                        vy: 0,\r\n                        size: Math.random() * 1.5 + 0.5,\r\n                    });\r\n                }\r\n            }\r\n        }\r\n        \r\n        ctx.clearRect(0, 0, width, height);\r\n    }\r\n\r\n    function draw() {\r\n        ctx.clearRect(0, 0, width, height);\r\n        \r\n        \/\/ --- PHYSIQUE SLOW & SMOOTH ---\r\n        time += 0.02;\r\n\r\n        ctx.fillStyle = particleColor;\r\n        \r\n        particles.forEach(p => {\r\n            \/\/ Ondulation\r\n            const waveX = Math.sin(time * 0.5 + p.y * 0.05) * 2;\r\n            const waveY = Math.cos(time * 0.3 + p.x * 0.05) * 2;\r\n            \r\n            \/\/ Interaction\r\n            const dx = mouse.x - p.x;\r\n            const dy = mouse.y - p.y;\r\n            const dist = Math.sqrt(dx*dx + dy*dy);\r\n            const radius = 80;\r\n            \r\n            let repelX = 0;\r\n            let repelY = 0;\r\n\r\n            if (dist < radius) {\r\n                const force = (radius - dist) \/ radius;\r\n                const angle = Math.atan2(dy, dx);\r\n                repelX = -Math.cos(angle) * force * 8;\r\n                repelY = -Math.sin(angle) * force * 8;\r\n            }\r\n\r\n            const targetX = p.originX + waveX;\r\n            const targetY = p.originY + waveY;\r\n\r\n            \/\/ Retour souple\r\n            const ax = (targetX - p.x) * 0.03;\r\n            const ay = (targetY - p.y) * 0.03;\r\n\r\n            p.vx += ax;\r\n            p.vy += ay;\r\n            p.vx += repelX * 0.3;\r\n            p.vy += repelY * 0.3;\r\n            \r\n            \/\/ Friction\r\n            p.vx *= 0.90; \r\n            p.vy *= 0.90;\r\n\r\n            p.x += p.vx;\r\n            p.y += p.vy;\r\n\r\n            ctx.beginPath();\r\n            ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);\r\n            ctx.fill();\r\n        });\r\n\r\n        requestAnimationFrame(draw);\r\n    }\r\n\r\n    const updateMouse = (e) => {\r\n        const r = canvas.getBoundingClientRect();\r\n        const cx = e.touches ? e.touches[0].clientX : e.clientX;\r\n        const cy = e.touches ? e.touches[0].clientY : e.clientY;\r\n        mouse.x = cx - r.left;\r\n        mouse.y = cy - r.top;\r\n    };\r\n\r\n    window.addEventListener('resize', init);\r\n    canvas.addEventListener('mousemove', updateMouse);\r\n    canvas.addEventListener('touchmove', updateMouse, {passive: true});\r\n    canvas.addEventListener('mouseleave', () => { mouse.x = -1000; mouse.y = -1000; });\r\n    canvas.addEventListener('touchend', () => { mouse.x = -1000; mouse.y = -1000; });\r\n\r\n    setTimeout(init, 100);\r\n    draw();\r\n})();\r\n<\/script>"},"pt":[],"pipeline":[34],"class_list":["post-3316","portfolio","type-portfolio","status-publish","has-post-thumbnail","hentry","pipeline-innov"],"_links":{"self":[{"href":"https:\/\/test.innovacore.group\/zh-hans\/wp-json\/wp\/v2\/portfolio\/3316","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/test.innovacore.group\/zh-hans\/wp-json\/wp\/v2\/portfolio"}],"about":[{"href":"https:\/\/test.innovacore.group\/zh-hans\/wp-json\/wp\/v2\/types\/portfolio"}],"version-history":[{"count":2,"href":"https:\/\/test.innovacore.group\/zh-hans\/wp-json\/wp\/v2\/portfolio\/3316\/revisions"}],"predecessor-version":[{"id":3320,"href":"https:\/\/test.innovacore.group\/zh-hans\/wp-json\/wp\/v2\/portfolio\/3316\/revisions\/3320"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/test.innovacore.group\/zh-hans\/wp-json\/wp\/v2\/media\/2369"}],"wp:attachment":[{"href":"https:\/\/test.innovacore.group\/zh-hans\/wp-json\/wp\/v2\/media?parent=3316"}],"wp:term":[{"taxonomy":"pt","embeddable":true,"href":"https:\/\/test.innovacore.group\/zh-hans\/wp-json\/wp\/v2\/pt?post=3316"},{"taxonomy":"pipeline","embeddable":true,"href":"https:\/\/test.innovacore.group\/zh-hans\/wp-json\/wp\/v2\/pipeline?post=3316"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}