<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Connect your sources once</title>
<style>
:root{ --bg:#eceffb; --tile:#ffffff; }
*{box-sizing:border-box}
html,body{height:100%}
body{margin:0;background:var(--bg);overflow:hidden;display:flex;align-items:center;justify-content:center}
/* Grille 5x5, remplit 100% du cadre.
Proportions Figma : 5 x 71,6 + 4 x 8 = 390 de base. */
.cg{
container-type:size;
position:relative;width:min(100vw,100vh);height:min(100vw,100vh);flex:0 0 auto;
display:grid;
grid-template-columns:repeat(5,minmax(0,1fr));
grid-template-rows:repeat(5,minmax(0,1fr));
gap:8px; gap:min(2.051cqw,2.051cqh);
}
.cg::before{
content:"";position:absolute;inset:-15%;pointer-events:none;z-index:0;filter:blur(6px);
background:radial-gradient(circle var(--glow-r,240px) at var(--mx,50%) var(--my,50%),
rgba(255,255,255,.85) 0%, rgba(255,255,255,.35) 38%, transparent 70%);
}
.t{
container-type:size;position:relative;z-index:1;min-width:0;min-height:0;
border-radius:11px; border-radius:min(2.941cqw,2.941cqh);
display:grid;place-items:center;background:var(--tile);
box-shadow:0 1px 2px rgba(24,24,24,calc(.05*var(--f,0)+.015)),
0 10px 24px -12px rgba(24,24,24,calc(.22*var(--f,0)));
opacity:var(--o,.2);transform:scale(var(--s,1));will-change:opacity,transform;
}
/* boîte logo : carrée, calée sur le plus petit côté de la tuile */
.t img{
width:min(50cqw,50cqh);height:min(50cqw,50cqh);
object-fit:contain;display:block;
}
</style>
</head>
<body>
<div class="cg" id="cg"></div>
<script>
/* Logos : assets Webflow de la collection Integrations (URLs permanentes).
`null` = tuile de texture, volontairement sans logo et plafonnée en opacité. */
var C = "https://cdn.prod.website-files.com/68f7335258cfb6a552b971c6/";
var G = [
C+"6a9818983bc239ab899dc355_free-confluence-logo-icon-svg-download-png-3029929.webp",
C+"6a882c6af1d6d4911f655a93_Dropbox_Icon.svg.webp",
C+"6a9818e7fbc7d35266df41fc_Box%2C_Inc._logo.svg.webp",
C+"6a882a84c5a0da12f586dde1_microsoft-sharepoint.svg",
C+"6a9818fa7652cb3d5853f93c_monday-icon.svg",
C+"6a882af17bb939f0f8491565_Gong_idnaeh32xQ_0.svg",
C+"6a882b405ebe5ef18a5d9b18_hubspot-1.svg",
C+"6a882ada229a038f7593d98a_gmail.svg",
C+"6a882b4755fd78e105a9b830_salesforce.svg",
C+"6a882b0ff14ca1bcd138f3d1_google-meet.svg",
C+"6a98187ed1c675e12f7a6cef_free-table-gonflable-logo-icon-svg-download-png-1254387.webp",
C+"6a882ae317298597676335ca_microsoft-outlook.svg",
C+"6a882b2ee6f257c8d68aabe6_drive.svg",
C+"6a882c6487e34e168790f27f_slack-new-logo-logo.svg",
C+"6a9818a998f744dd105b1e1e_images%20(1).png",
C+"6a882c5dca12adb4682bb8bb_Microsoft-Dynamics-Sales-Marketing-and-Service-280x300-1.webp",
C+"6a882b284ab7b52c6e04c265_notion.svg",
C+"6a882a94272777380b707782_microsoft-teams.svg",
C+"6a882afc2e3d52c370d7609c_zoom.svg",
C+"6a882b16ff12241b47931562_Attio_idvlyMewyB_0.svg",
C+"6a9818d47652cb3d5853e234_images.jpeg",
C+"6a882b1ec5a0da12f5873c43_Folk_idCC19iYri_0.svg",
C+"6a98191298f744dd105b6a85_icon.webp",
C+"6a882b3526591d806f551f45_Pipedrive_id-7ejZnwv_0.svg",
C+"6a981f096edf4f338fe78699_Google_Calendar_icon_(2020).svg.webp"
];
var ALT = ["Confluence","Dropbox","Box","SharePoint","Monday","Gong","HubSpot","Gmail","Salesforce","Google Meet","Airtable","Outlook","Google Drive","Slack","Apollo","Dynamics 365","Notion","Microsoft Teams","Zoom","Attio","Fireflies","folk","Lemlist","Pipedrive","Google Calendar"];
/* Réglages validés le 27/08 */
var cfg = { radius:163, minOp:.20, curve:2.09, ease:.11, scale:.06, idle:true };
var BLANK_CAP = .45; /* plafond d'opacité des tuiles sans logo */
var cg = document.getElementById('cg'), tiles = [], caps = [];
for (var i = 0; i < 25; i++){
var el = document.createElement('div');
el.className = 't';
if (G[i]) el.innerHTML = '<img src="' + G[i] + '" alt="' + ALT[i] + '" loading="eager">';
cg.appendChild(el);
tiles.push(el); caps.push(G[i] ? 1 : BLANK_CAP);
}
var pointer={x:0,y:0}, smooth={x:0,y:0}, hover=0, hoverTarget=0, centers=[], box=null;
var t0 = performance.now();
var reduced = matchMedia('(prefers-reduced-motion: reduce)').matches;
function measure(){
box = cg.getBoundingClientRect();
centers = tiles.map(function(el){
var r = el.getBoundingClientRect();
return { x:r.left-box.left+r.width/2, y:r.top-box.top+r.height/2 };
});
if(!hover){ smooth.x=box.width/2; smooth.y=box.height/2; pointer={x:smooth.x,y:smooth.y}; }
}
cg.addEventListener('pointermove', function(e){
var r = cg.getBoundingClientRect();
pointer.x = e.clientX - r.left; pointer.y = e.clientY - r.top; hoverTarget = 1;
});
cg.addEventListener('pointerleave', function(){ hoverTarget = 0; });
function lerp(a,b,t){ return a+(b-a)*t; }
function clamp(v,a,b){ return Math.min(b,Math.max(a,v)); }
function frame(now){
var time = (now-t0)/1000;
if(hoverTarget === 0 && box){
if(cfg.idle && !reduced){
pointer.x = box.width/2 + Math.cos(time*.38)*box.width *.30;
pointer.y = box.height/2 + Math.sin(time*.27)*box.height*.26;
} else { pointer.x = box.width/2; pointer.y = box.height/2; }
}
var k = reduced ? 1 : cfg.ease;
smooth.x = lerp(smooth.x, pointer.x, k);
smooth.y = lerp(smooth.y, pointer.y, k);
hover = lerp(hover, hoverTarget, k*.8);
var scale = box ? Math.min(box.width, box.height)/390 : 1;
var R = cfg.radius * scale * lerp(1.45, 1, hover);
for(var i=0;i<tiles.length;i++){
var c = centers[i]; if(!c) continue;
var d = Math.hypot(c.x-smooth.x, c.y-smooth.y)/R;
var t = clamp(1-d,0,1); t = t*t*(3-2*t);
var f = Math.pow(t, cfg.curve);
var cap = caps[i], st = tiles[i].style;
st.setProperty('--f', (f*cap).toFixed(3));
st.setProperty('--o', ((cfg.minOp + (1-cfg.minOp)*f) * cap).toFixed(3));
st.setProperty('--s', (1 + cfg.scale*f*cap).toFixed(4));
}
if(box){
cg.style.setProperty('--mx', ((smooth.x + box.width *.15)/(box.width *1.3)*100).toFixed(2)+'%');
cg.style.setProperty('--my', ((smooth.y + box.height*.15)/(box.height*1.3)*100).toFixed(2)+'%');
cg.style.setProperty('--glow-r', Math.round(R*1.1)+'px');
}
requestAnimationFrame(frame);
}
addEventListener('resize', measure);
addEventListener('load', measure);
measure();
requestAnimationFrame(frame);
</script>
</body>
</html>