Sable/ source
Open live demo ↗

Single-file build — HTML + CSS + vanilla JS + Three.js particle hero. 305 lines, 19 KB. Zero backend, zero tracking.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Sable</title>
<meta name="description" content="Sable — the AI analytics platform that turns raw signal into foresight.">
<meta name="robots" content="noindex">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Sora:wght@500;600;700&family=Manrope:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
  :root{
    --bg:#06070d; --bg2:#0a0c15; --panel:rgba(255,255,255,.028); --line:rgba(140,155,200,.14);
    --ink:#eef1fa; --mute:#9aa4bd; --dim:#5f6883;
    --teal:#5eead4; --indigo:#8b8cf9; --violet:#b078ff;
    --disp:'Sora',system-ui,sans-serif; --body:'Manrope',system-ui,sans-serif; --mono:'IBM Plex Mono',ui-monospace,monospace;
    --max:1180px;
  }
  *{box-sizing:border-box}
  body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--body);-webkit-font-smoothing:antialiased;line-height:1.55;overflow-x:hidden}
  a{color:inherit;text-decoration:none}
  h1,h2,h3{font-family:var(--disp);font-weight:600;letter-spacing:-.02em;text-wrap:balance;margin:0}
  .wrap{max-width:var(--max);margin:0 auto;padding:0 26px}
  .eyebrow{font-family:var(--mono);font-size:12px;letter-spacing:.24em;text-transform:uppercase;color:var(--teal)}
  .btn{display:inline-flex;align-items:center;gap:9px;font-family:var(--disp);font-weight:600;font-size:14.5px;
    padding:12px 22px;border-radius:8px;cursor:pointer;border:1px solid transparent;transition:transform .15s,box-shadow .2s,background .2s}
  .btn-p{background:linear-gradient(100deg,var(--teal),var(--indigo));color:#06070d;box-shadow:0 6px 30px rgba(94,234,212,.22)}
  .btn-p:hover{transform:translateY(-2px);box-shadow:0 10px 40px rgba(139,140,249,.35)}
  .btn-g{border-color:var(--line);color:var(--ink);background:rgba(255,255,255,.02)}
  .btn-g:hover{border-color:rgba(140,155,200,.4);background:rgba(255,255,255,.05)}

  /* nav */
  nav{position:fixed;top:0;left:0;right:0;z-index:50;backdrop-filter:blur(14px);background:rgba(6,7,13,.55);border-bottom:1px solid transparent;transition:border-color .3s,background .3s}
  nav.scrolled{border-bottom-color:var(--line);background:rgba(6,7,13,.82)}
  .nav-in{display:flex;align-items:center;justify-content:space-between;height:66px}
  .logo{display:flex;align-items:center;gap:11px;font-family:var(--disp);font-weight:700;font-size:19px;letter-spacing:-.01em}
  .mark{width:26px;height:26px;border-radius:7px;background:linear-gradient(135deg,var(--teal),var(--indigo));display:grid;place-items:center;box-shadow:0 0 20px rgba(94,234,212,.4)}
  .mark:before{content:'';width:9px;height:9px;background:#06070d;border-radius:2px;transform:rotate(45deg)}
  .nav-links{display:flex;gap:30px;font-size:14px;color:var(--mute)}
  .nav-links a:hover{color:var(--ink)}
  @media(max-width:820px){.nav-links{display:none}}

  /* hero */
  .hero{position:relative;min-height:100vh;display:flex;align-items:center;padding-top:66px;overflow:hidden}
  #scene{position:absolute;inset:0;z-index:0}
  .glow{position:absolute;z-index:0;width:900px;height:900px;top:-160px;right:-220px;border-radius:50%;
    background:radial-gradient(circle,rgba(94,234,212,.14),rgba(139,140,249,.08) 40%,transparent 66%);filter:blur(20px);pointer-events:none}
  .hero-grid{position:relative;z-index:2;display:grid;grid-template-columns:1.05fr .95fr;gap:20px;align-items:center;width:100%}
  @media(max-width:900px){.hero-grid{grid-template-columns:1fr}}
  .hero h1{font-size:clamp(38px,6vw,72px);line-height:1.02;font-weight:700;margin:20px 0 0}
  .hero h1 .grad{background:linear-gradient(100deg,var(--teal),var(--indigo) 55%,var(--violet));-webkit-background-clip:text;background-clip:text;color:transparent}
  .lead{color:var(--mute);font-size:clamp(16px,2vw,19px);max-width:40ch;margin:22px 0 30px}
  .cta-row{display:flex;gap:14px;flex-wrap:wrap}
  .trust{display:flex;gap:34px;margin-top:44px;flex-wrap:wrap}
  .trust .n{font-family:var(--disp);font-weight:700;font-size:26px}
  .trust .l{font-size:12.5px;color:var(--dim);font-family:var(--mono);letter-spacing:.05em}
  .scrollcue{position:absolute;bottom:26px;left:50%;transform:translateX(-50%);z-index:2;font-family:var(--mono);font-size:11px;letter-spacing:.3em;color:var(--dim);text-transform:uppercase}

  /* sections */
  section{position:relative;z-index:1}
  .band{padding:110px 0}
  .sec-head{max-width:620px;margin-bottom:52px}
  .sec-head h2{font-size:clamp(28px,4vw,44px);line-height:1.08;margin-top:14px}
  .sec-head p{color:var(--mute);font-size:17px;margin:16px 0 0}

  .features{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}
  @media(max-width:900px){.features{grid-template-columns:1fr}}
  .card{border:1px solid var(--line);border-radius:14px;padding:26px;background:linear-gradient(180deg,var(--panel),transparent);transition:border-color .25s,transform .25s}
  .card:hover{border-color:rgba(94,234,212,.35);transform:translateY(-3px)}
  .card .ic{width:42px;height:42px;border-radius:10px;display:grid;place-items:center;border:1px solid var(--line);color:var(--teal);margin-bottom:18px}
  .card h3{font-size:19px;margin-bottom:9px}
  .card p{color:var(--mute);font-size:14.5px;margin:0}
  .card .k{font-family:var(--mono);font-size:11px;color:var(--dim);letter-spacing:.16em;text-transform:uppercase;display:block;margin-bottom:14px}

  /* split showcase */
  .split{display:grid;grid-template-columns:1fr 1fr;gap:56px;align-items:center}
  @media(max-width:900px){.split{grid-template-columns:1fr;gap:34px}}
  .panel-box{border:1px solid var(--line);border-radius:16px;background:linear-gradient(160deg,rgba(94,234,212,.05),rgba(139,140,249,.04));padding:22px;position:relative;overflow:hidden}
  .panel-box .row{display:flex;justify-content:space-between;align-items:center;padding:12px 4px;border-bottom:1px solid var(--line);font-size:13.5px}
  .panel-box .row:last-child{border-bottom:none}
  .panel-box .tag{font-family:var(--mono);font-size:11px;padding:3px 9px;border-radius:20px;border:1px solid var(--line);color:var(--teal)}
  .panel-box .tag.warn{color:var(--violet);border-color:rgba(176,120,255,.4)}
  .chart{height:120px;margin:14px 0 4px}
  .split ul{list-style:none;padding:0;margin:22px 0 0}
  .split li{display:flex;gap:13px;padding:11px 0;color:var(--mute);font-size:15px;align-items:flex-start}
  .split li b{color:var(--ink);font-weight:600}
  .dot{width:7px;height:7px;border-radius:50%;background:var(--teal);margin-top:8px;flex:0 0 auto;box-shadow:0 0 8px var(--teal)}

  /* stats band */
  .statband{border-top:1px solid var(--line);border-bottom:1px solid var(--line);background:var(--bg2)}
  .stats{display:grid;grid-template-columns:repeat(4,1fr);gap:20px;padding:56px 0}
  @media(max-width:760px){.stats{grid-template-columns:repeat(2,1fr);gap:34px}}
  .stat .n{font-family:var(--disp);font-weight:700;font-size:clamp(30px,4vw,46px);background:linear-gradient(120deg,var(--teal),var(--indigo));-webkit-background-clip:text;background-clip:text;color:transparent}
  .stat .l{color:var(--mute);font-size:13.5px;margin-top:6px}

  /* cta */
  .cta{text-align:center;position:relative;overflow:hidden}
  .cta .glow2{position:absolute;inset:0;z-index:0;background:radial-gradient(600px 300px at 50% 0%,rgba(139,140,249,.16),transparent 70%)}
  .cta-in{position:relative;z-index:1}
  .cta h2{font-size:clamp(30px,5vw,52px);line-height:1.05}
  .cta p{color:var(--mute);font-size:18px;max-width:46ch;margin:18px auto 32px}

  footer{border-top:1px solid var(--line);padding:44px 0;color:var(--dim);font-size:13.5px}
  .foot-in{display:flex;justify-content:space-between;gap:20px;flex-wrap:wrap;align-items:center}
  .foot-links{display:flex;gap:24px}
  .foot-links a:hover{color:var(--ink)}

  .reveal{opacity:0;transform:translateY(22px);transition:opacity .7s cubic-bezier(.2,.7,.2,1),transform .7s cubic-bezier(.2,.7,.2,1)}
  .reveal.in{opacity:1;transform:none}
  @media(prefers-reduced-motion:reduce){.reveal{opacity:1;transform:none;transition:none}}
</style>
</head>
<body>

<nav id="nav"><div class="wrap nav-in">
  <div class="logo"><span class="mark"></span>Sable</div>
  <div class="nav-links">
    <a href="#platform">Platform</a><a href="#signal">Signal Engine</a><a href="#numbers">Results</a><a href="#start">Pricing</a>
  </div>
  <a class="btn btn-g" href="#start">Request access</a>
</div></nav>

<header class="hero">
  <div id="scene"></div>
  <div class="glow"></div>
  <div class="wrap hero-grid">
    <div>
      <span class="eyebrow">Predictive analytics · autonomous</span>
      <h1>See the number<br>before it <span class="grad">moves.</span></h1>
      <p class="lead">Sable is the AI analytics platform that reads every stream your business runs on, separates signal from noise, and tells you what happens next — in plain language, in real time.</p>
      <div class="cta-row">
        <a class="btn btn-p" href="#start">Request access →</a>
        <a class="btn btn-g" href="#platform">See the platform</a>
      </div>
      <div class="trust">
        <div><div class="n">2.4B</div><div class="l">events / day</div></div>
        <div><div class="n">98.6%</div><div class="l">forecast precision</div></div>
        <div><div class="n">&lt;300ms</div><div class="l">signal latency</div></div>
      </div>
    </div>
    <div></div>
  </div>
  <div class="scrollcue">Scroll</div>
</header>

<section id="platform" class="band">
  <div class="wrap">
    <div class="sec-head reveal">
      <span class="eyebrow">The platform</span>
      <h2>One layer of intelligence over all of your data.</h2>
      <p>Connect a warehouse, a stream, or a spreadsheet. Sable models it in minutes and starts surfacing what your dashboards can't.</p>
    </div>
    <div class="features">
      <div class="card reveal"><div class="ic">◇</div><span class="k">Forecasting</span><h3>Predictive models, self-tuning</h3><p>Sable builds and retrains a forecast for every metric that matters, adapting the moment your data shifts — no data-science team required.</p></div>
      <div class="card reveal"><div class="ic">◈</div><span class="k">Detection</span><h3>Anomalies, caught early</h3><p>It watches millions of series at once and flags the one moving wrong hours before it shows up on a chart — with the likely cause attached.</p></div>
      <div class="card reveal"><div class="ic">◆</div><span class="k">Language</span><h3>Ask in plain English</h3><p>“Why did churn spike in the Nordics?” Sable answers with the drivers, the confidence, and the evidence — not another query to write.</p></div>
    </div>
  </div>
</section>

<section id="signal" class="band" style="padding-top:20px">
  <div class="wrap split">
    <div class="reveal">
      <span class="eyebrow">Signal engine</span>
      <h2 style="font-size:clamp(28px,4vw,42px);line-height:1.08;margin-top:14px">Foresight, not hindsight.</h2>
      <ul>
        <li><span class="dot"></span><span><b>Live signal, not batch reports.</b> Streaming inference scores every event as it lands — sub-300ms from source to insight.</span></li>
        <li><span class="dot"></span><span><b>Cause, not just correlation.</b> Sable traces the drivers behind a move and ranks them by impact and confidence.</span></li>
        <li><span class="dot"></span><span><b>Explainable by design.</b> Every forecast ships with the evidence behind it, so your team acts on it without second-guessing.</span></li>
      </ul>
    </div>
    <div class="reveal">
      <div class="panel-box">
        <div class="row"><span style="font-family:var(--mono);font-size:12px;color:var(--mute)">signal / revenue.eu</span><span class="tag">▲ forecast +6.2%</span></div>
        <canvas class="chart" id="spark"></canvas>
        <div class="row"><span>Churn · Nordics</span><span class="tag warn">anomaly · 2h early</span></div>
        <div class="row"><span>Pipeline velocity</span><span class="tag">stable · 94% conf</span></div>
        <div class="row"><span>Ad spend efficiency</span><span class="tag warn">drift detected</span></div>
      </div>
    </div>
  </div>
</section>

<div class="statband" id="numbers"><div class="wrap"><div class="stats">
  <div class="stat reveal"><div class="n">40×</div><div class="l">faster to first insight</div></div>
  <div class="stat reveal"><div class="n">6 hrs</div><div class="l">average early-warning lead</div></div>
  <div class="stat reveal"><div class="n">120+</div><div class="l">connectors, zero code</div></div>
  <div class="stat reveal"><div class="n">$1.2B</div><div class="l">revenue steered in 2025</div></div>
</div></div></div>

<section class="band">
  <div class="wrap split">
    <div class="reveal">
      <div class="panel-box" style="background:linear-gradient(160deg,rgba(176,120,255,.06),rgba(94,234,212,.04))">
        <div style="font-family:var(--mono);font-size:12px;color:var(--mute);margin-bottom:12px">// natural-language query</div>
        <div style="font-size:16px;color:var(--ink);margin-bottom:16px">“Where are we leaking margin this quarter?”</div>
        <div style="border-top:1px solid var(--line);padding-top:14px;font-size:14.5px;color:var(--mute);line-height:1.7">
          <span style="color:var(--teal)">Sable</span> — Fulfilment in <b style="color:var(--ink)">DE-South</b> is the leak: expedited-shipping share rose 3.1× after a carrier SLA change on Aug 4. Projected margin drag <b style="color:var(--ink)">-$418k</b> by quarter close. Confidence 91%.
        </div>
      </div>
    </div>
    <div class="reveal">
      <span class="eyebrow">Built for the room where it's decided</span>
      <h2 style="font-size:clamp(28px,4vw,42px);line-height:1.08;margin-top:14px">From raw stream to the decision, in one breath.</h2>
      <p style="color:var(--mute);font-size:16px;margin-top:16px">No pipelines to babysit, no dashboards to reverse-engineer. Sable sits over your stack and hands the people who decide a clear, evidenced answer — the moment it matters.</p>
      <div class="cta-row" style="margin-top:26px"><a class="btn btn-g" href="#start">Book a walkthrough</a></div>
    </div>
  </div>
</section>

<section id="start" class="band cta">
  <div class="glow2"></div>
  <div class="wrap cta-in">
    <span class="eyebrow">Limited early access</span>
    <h2 style="margin-top:16px">Put the next quarter<br>in front of you.</h2>
    <p>Sable is onboarding a small cohort of data-serious teams. Bring one metric that keeps you up at night — we'll forecast it live.</p>
    <a class="btn btn-p" href="#" style="font-size:16px;padding:15px 30px">Request access →</a>
  </div>
</section>

<footer><div class="wrap foot-in">
  <div class="logo" style="font-size:16px"><span class="mark" style="width:22px;height:22px"></span>Sable</div>
  <div class="foot-links"><a href="#platform">Platform</a><a href="#signal">Signal Engine</a><a href="#start">Access</a><a href="#">Security</a></div>
  <div>© 2026 Sable Intelligence</div>
</div></footer>

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.160.0/three.min.js"></script>
<script>
(function(){
  "use strict";
  var io=new IntersectionObserver(function(es){es.forEach(function(e){if(e.isIntersecting){e.target.classList.add('in');io.unobserve(e.target);}});},{threshold:.14});
  document.querySelectorAll('.reveal').forEach(function(el){io.observe(el);});
  var nav=document.getElementById('nav');
  addEventListener('scroll',function(){nav.classList.toggle('scrolled',scrollY>20);},{passive:true});

  (function(){
    var c=document.getElementById('spark'); if(!c)return; var ctx=c.getContext('2d');
    function draw(){
      var w=c.clientWidth,h=c.clientHeight,dpr=Math.min(2,devicePixelRatio||1); c.width=w*dpr;c.height=h*dpr;ctx.setTransform(dpr,0,0,dpr,0,0);
      ctx.clearRect(0,0,w,h);
      var pts=[],n=48; for(var i=0;i<n;i++){var t=i/(n-1); pts.push([t*w, h*(0.75-0.42*Math.sin(t*7)*Math.exp(-t*0.4)-t*0.28)]);}
      var g=ctx.createLinearGradient(0,0,w,0); g.addColorStop(0,'#5eead4');g.addColorStop(1,'#8b8cf9');
      var fill=ctx.createLinearGradient(0,0,0,h); fill.addColorStop(0,'rgba(94,234,212,.28)');fill.addColorStop(1,'rgba(94,234,212,0)');
      ctx.beginPath();ctx.moveTo(pts[0][0],pts[0][1]); pts.forEach(function(p){ctx.lineTo(p[0],p[1]);});
      ctx.lineTo(w,h);ctx.lineTo(0,h);ctx.closePath();ctx.fillStyle=fill;ctx.fill();
      ctx.beginPath();ctx.moveTo(pts[0][0],pts[0][1]); pts.forEach(function(p){ctx.lineTo(p[0],p[1]);});
      ctx.strokeStyle=g;ctx.lineWidth=2.2;ctx.lineJoin='round';ctx.stroke();
      var last=pts[pts.length-1]; ctx.beginPath();ctx.arc(last[0],last[1],3.4,0,7);ctx.fillStyle='#8b8cf9';ctx.shadowColor='#8b8cf9';ctx.shadowBlur=12;ctx.fill();ctx.shadowBlur=0;
    }
    draw(); addEventListener('resize',draw);
  })();

  if(!window.THREE) return;
  var reduce=matchMedia('(prefers-reduced-motion:reduce)').matches;
  var host=document.getElementById('scene');
  var renderer=new THREE.WebGLRenderer({antialias:true,alpha:true,powerPreference:'high-performance'});
  renderer.setPixelRatio(Math.min(2,devicePixelRatio||1));
  var scene=new THREE.Scene();
  var cam=new THREE.PerspectiveCamera(52,1,.1,100); cam.position.set(0,0,15.5);
  host.appendChild(renderer.domElement);

  var N=reduce?1400:3200, R=7.6, pos=new Float32Array(N*3), col=new Float32Array(N*3), rnd=new Float32Array(N);
  var ga=Math.PI*(3-Math.sqrt(5)), cA=new THREE.Color(0x5eead4), cB=new THREE.Color(0x8b8cf9), cC=new THREE.Color(0xb078ff), tmp=new THREE.Color();
  for(var i=0;i<N;i++){
    var y=1-(i/(N-1))*2, r=Math.sqrt(1-y*y), th=ga*i;
    pos[i*3]=Math.cos(th)*r*R; pos[i*3+1]=y*R; pos[i*3+2]=Math.sin(th)*r*R;
    var m=(y+1)/2; tmp.copy(m<0.5?cA:cB).lerp(m<0.5?cB:cC,(m%0.5)*2);
    col[i*3]=tmp.r;col[i*3+1]=tmp.g;col[i*3+2]=tmp.b; rnd[i]=Math.random()*6.28;
  }
  var geo=new THREE.BufferGeometry();
  geo.setAttribute('position',new THREE.BufferAttribute(pos,3));
  geo.setAttribute('color',new THREE.BufferAttribute(col,3));
  geo.setAttribute('aRnd',new THREE.BufferAttribute(rnd,1));
  var mat=new THREE.ShaderMaterial({
    transparent:true,depthWrite:false,blending:THREE.AdditiveBlending,
    uniforms:{uT:{value:0}},
    vertexShader:'attribute float aRnd;varying vec3 vC;uniform float uT;void main(){vC=color;vec4 mv=modelViewMatrix*vec4(position,1.);float tw=.6+.4*sin(uT*1.5+aRnd);gl_PointSize=(1.7+1.7*tw)*(300./-mv.z);gl_Position=projectionMatrix*mv;}',
    fragmentShader:'varying vec3 vC;void main(){vec2 d=gl_PointCoord-vec2(.5);float a=smoothstep(.5,.0,length(d));gl_FragColor=vec4(vC,a);}',
    vertexColors:true
  });
  var pts=new THREE.Points(geo,mat); scene.add(pts);
  var D=reduce?200:600, dp=new Float32Array(D*3);
  for(var j=0;j<D;j++){dp[j*3]=(Math.random()-.5)*40;dp[j*3+1]=(Math.random()-.5)*26;dp[j*3+2]=(Math.random()-.5)*20-6;}
  var dg=new THREE.BufferGeometry();dg.setAttribute('position',new THREE.BufferAttribute(dp,3));
  var dust=new THREE.Points(dg,new THREE.PointsMaterial({color:0x8b8cf9,size:.045,transparent:true,opacity:.5,depthWrite:false}));
  scene.add(dust);

  var mx=0,my=0,tmx=0,tmy=0;
  addEventListener('pointermove',function(e){tmx=(e.clientX/innerWidth-.5);tmy=(e.clientY/innerHeight-.5);},{passive:true});
  function size(){var w=host.clientWidth,h=host.clientHeight;renderer.setSize(w,h,false);cam.aspect=w/h;cam.updateProjectionMatrix();}
  size(); addEventListener('resize',size);
  var t0=performance.now();
  (function loop(now){
    var t=(now-t0)/1000; mat.uniforms.uT.value=t;
    mx+=(tmx-mx)*.04; my+=(tmy-my)*.04;
    if(!reduce){pts.rotation.y=t*.08+mx*.5; pts.rotation.x=-.15+my*.35; dust.rotation.y=t*.02;}
    else{pts.rotation.y=mx*.5;pts.rotation.x=-.15+my*.35;}
    renderer.render(scene,cam); requestAnimationFrame(loop);
  })(t0);
})();
</script>

</body></html>