Make it stand out

Introduce your brand

<div class="zakini-intro" id="zakini-intro">
  <div class="panel left">
    <img class="panel-img" alt="Left panel"
      src="https://static1.squarespace.com/static/689991212a7345610c783fa7/t/68c93729b852960b1ce44e20/1758017321361/Group+9.png">
  </div>

  <div class="panel right">
    <img class="panel-img" alt="Right panel"
      src="https://static1.squarespace.com/static/689991212a7345610c783fa7/t/68c93742dd55742bd2309962/1758017346783/Group+10.png">
  </div>

  <img class="logo" alt="ZAKINI"
    src="https://static1.squarespace.com/static/689991212a7345610c783fa7/t/68c9376e319c0108a9a42eb6/1758017390626/Group+11.png" />
</div>

<style>
/* Stage */
#zakini-intro{
  position: relative;
  height: 100vh;
  overflow: hidden;
  background: transparent;
}

/* Panels: visible at load (screenshot start) */
#zakini-intro .panel{
  position: absolute;
  top: 50%;
  transform: translateY(-50%);    /* no X shift initially */
  width: clamp(140px, 21.8vw, 419px);
  height: clamp(64px, 11.5vw, 222px);
  z-index: 2;
  will-change: transform;
}
#zakini-intro .panel.left  { left: 0; }
#zakini-intro .panel.right { right: 0; }

#zakini-intro .panel-img{
  width: 100%; height: 100%;
  object-fit: contain;
  pointer-events: none; user-select: none;
}

/* Logo: centered, scales .5 -> 1; JS caps final width to fit the diamond */
#zakini-intro .logo{
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%) scale(.5);
  width: 200px;                 /* temporary; JS will set a safe max-width */
  height: auto;
  opacity: 0;
  z-index: 3;
  pointer-events: none; user-select: none;
  will-change: transform, opacity, width;
}

@media (prefers-reduced-motion: reduce){
  .panel, .logo{ transition: none !important; animation: none !important; }
}
</style>

<!-- GSAP + ScrollTrigger -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js"></script>

<script>
document.addEventListener("DOMContentLoaded", () => {
  gsap.registerPlugin(ScrollTrigger);

  // ---- Tunables ----
  // ---- Tunables (target the exact stop position) ----
  // Desired diamond width at the center (where the tips overlap).
  // It's a clamped value: min .. preferred (as vw of the section) .. max
  const DIAMOND_MIN_PX   = 40;     // try 38–46 to make it smaller/bigger
  const DIAMOND_PREF_VW  = 0.8;  // 2.6% of section width (raise -> larger diamond)
  const DIAMOND_MAX_PX   = 120;     // upper cap

  const $section = document.getElementById("zakini-intro");
  const $left    = document.querySelector("#zakini-intro .panel.left");
  const $right   = document.querySelector("#zakini-intro .panel.right");
  const $logo    = document.querySelector("#zakini-intro .logo");

  // compute distances each refresh (clamp sizes change with viewport)
  function metrics() {
    const sw       = $section.clientWidth;      // section width (panels are positioned in this space)
    const center   = sw / 2;
    const pw       = $left.offsetWidth;         // panel width
    const ph       = $left.offsetHeight;        // panel height (not strictly needed now)

    // distance so inner edge hits the exact center (no overlap)
    const toCenter = center - pw;

    // target diamond width (clamped)
    const preferred = sw * DIAMOND_PREF_VW;                // % of section width
    const overlap   = Math.max(DIAMOND_MIN_PX, Math.min(preferred, DIAMOND_MAX_PX));

    // move each panel half of the diamond width past center
    const leftX     = toCenter + overlap / 2;
    const rightX    = -(toCenter + overlap / 2);

    // keep logo inside the diamond (with a little padding)
    const LOGO_PADDING_PX = 0;
    const logoMax   = Math.max(0, overlap - LOGO_PADDING_PX * 2);

    return { leftX, rightX, overlap, logoMax };
  }


  // set initial safe logo width (also on resize)
  function sizeLogo() {
    const { logoMax } = metrics();
    $logo.style.maxWidth = logoMax + "px";
    $logo.style.width    = Math.min(160, logoMax) + "px"; // base size, still capped
  }

  sizeLogo();

  const tl = gsap.timeline({
    scrollTrigger: {
      trigger: "#zakini-intro",
      start: "top top",
      end: "+=150%",
      scrub: true,
      pin: true,
      anticipatePin: 1,
      invalidateOnRefresh: true,
      onRefresh: sizeLogo
    },
    defaults: { ease: "power3.out" }
  });

  // Panels slide inward and stop overlapped, forming the diamond
  tl.to("#zakini-intro .panel.left",  {
      x: () => metrics().leftX,
      duration: 1
    }, 0)
    .to("#zakini-intro .panel.right", {
      x: () => metrics().rightX,
      duration: 1
    }, 0)

    // Logo appears at the end, scaling .5 -> 1 and staying inside diamond
    .to("#zakini-intro .logo", {
      opacity: 1,
      scale: 1,
      duration: 0.35
    }, 0.90); // later = closer to end; adjust 0.90..0.98 to taste

  // Re-measure when everything loads
  window.addEventListener("load", () => {
    sizeLogo();
    ScrollTrigger.refresh();
  });

  // Re-measure on resize as clamps change
  window.addEventListener("resize", sizeLogo);
});
</script>

 

<div class="timeline-wrapper" id="half-arc-timeline">
  <div class="pin">

    <!-- static heading -->
    <h2 class="static-heading">
      We Work With the People <br>Who Shape Environments.
    </h2>

    <div class="viewport">
      <div class="ring" aria-hidden="true">
        <div class="ring-item"><span class="ring-num">1</span></div>
        <div class="ring-item"><span class="ring-num">2</span></div>
        <div class="ring-item"><span class="ring-num">3</span></div>
        <div class="ring-item"><span class="ring-num">4</span></div>
        <div class="ring-item"><span class="ring-num">5</span></div>
      </div>

      <div class="center-text">
        <h2 class="title">Instant</h2>
        <p class="desc">What once took months, now happens in moments.</p>
        <a href="#" class="btn">Learn More</a>
      </div>
    </div>
  </div>
</div>

<style>
  /* scoped vars */
  #half-arc-timeline{
    --ring: min(140vmin, 1920px); /* diameter */
    --dot: 64px;
    --outside: 48px;               /* how far big numbers sit outside the arc */
    --tick: 8px;                    /* <— size of the small moving dots */
    --stroke: 2px;
    --accent: #fff;
    --fg: #e7ecf6;
    --muted: #aeb7c2;
    --bg: rgba(2, 1, 34, 1);

    background: var(--bg);
    position: relative;
  }

  .after-content{ background:#0a0a0a; padding: 480vh 6vw; color:#e7ecf6; }
  .after-content .container{ max-width: 900px; margin: 0 auto; }

  /* heading */
  #half-arc-timeline .static-heading{
    position: absolute;
    top: 6%;
    left: 50%;
    transform: translateX(-50%);
    margin: 0;
    width: min(88vw, 1000px);
    text-align: center;
    color: var(--fg);
    font-size: clamp(20px, 3vw, 36px);
    font-weight: 600;
    line-height: 1.2;
    z-index: 3;
  }

  /* layout */
  #half-arc-timeline .pin{ position: relative; height: 100vh; }
  #half-arc-timeline .viewport{
    position: absolute; left: 0; right: 0; bottom: 0;
    height: calc(var(--ring) / 2); overflow: visible;
  }

  /* ring */
  #half-arc-timeline .ring{
    --rot: -90deg; /* 1 at top on load */
    position: absolute; 
    left: 50%; 
    top: 90px; /* moved 48px higher than original */
    width: var(--ring); 
    height: var(--ring);
    transform: translateX(-50%);
    border-radius: 50%;
    box-shadow: inset 0 0 0 var(--stroke) rgba(255,255,255,.18);
  }

  /* spokes */
  #half-arc-timeline .ring-item{
    position: absolute; left: 50%; top: 50%;
    width: 1px; height: 1px; transform-origin: 0 0;
    transform: rotate(calc(var(--deg, 0deg) + var(--rot, 0deg)));
    transition: transform .35s cubic-bezier(.2,.7,.2,1);
  }

  /* numbers: OUTSIDE the arc */
  #half-arc-timeline .ring-num{
    display: grid; place-items: center;
    width: var(--dot); height: var(--dot); border-radius: 50%;
    border: 1px solid rgba(255,255,255,.5); color: var(--fg);
    background: rgba(0,0,0,.4); backdrop-filter: blur(3px);
    transform:
      translate(
        calc(var(--ring)/2 + var(--outside) - var(--stroke) - var(--dot)/2),
        calc(-1 * var(--dot)/2)
      )
      rotate(calc(-1 * (var(--deg, 0deg) + var(--rot, 0deg))));
    font-size: 14px; font-weight: 900; letter-spacing: 1.25px;
  }
  #half-arc-timeline .ring-item.is-active .ring-num{
    border-color: var(--accent); background: var(--accent); color: #111;
  }

  /* NEW: tiny dot sitting ON the arc (one per item) */
  #half-arc-timeline .ring-dot{
    position: absolute; left: 0; top: 0;
    width: var(--tick); height: var(--tick); border-radius: 50%;
    background: #fff;
    /* place on stroke (no counter-rotate needed) */
    transform:
      translate(
        calc(var(--ring)/2 - var(--stroke) - var(--tick)/2),
        calc(-1 * var(--tick)/2)
      );
    box-shadow: 0 0 0 1px rgba(255,255,255,.35) inset, 0 0 4px rgba(255,255,255,.25);
  }

  /* center text */
  #half-arc-timeline .center-text{
    position: absolute; left: 50%; top: 35%;
    transform: translateX(-50%); text-align: center;
    width: min(88vw, 720px); padding: 0 12px; z-index: 2; color: var(--fg);
  }
  #half-arc-timeline .center-text .title{
    margin: 0 0 .6rem; font-size: clamp(28px, 4.4vw, 52px); line-height: 1.1; transition: opacity .25s ease;
  }
  #half-arc-timeline .center-text .desc{
    margin: 0; color: var(--muted); font-size: clamp(14px, 1.3vw, 16px); transition: opacity .25s ease;
  }
  #half-arc-timeline .center-text .btn{
    display:inline-block; margin-top:1rem; padding:.6rem 1.2rem;
    background:#fff; color:#111; font-weight:900; text-transform:uppercase;
    border-radius:16px; text-decoration:none; transition:background .3s ease, opacity .25s ease;
    letter-spacing:1.25px; font-size:14px; line-height:1; padding:32px 64px; margin-top:clamp(32px,7.29vw,140px);
 border: 1px solid #fff;
  }
  #half-arc-timeline .center-text .btn:hover{ 
  background: transparent; 
  color: #fff;
  }
</style>

<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"></script>

<script>
/* start at top on reload/return */
if ('scrollRestoration' in history) history.scrollRestoration = 'manual';
function jumpToTop(){ window.scrollTo(0,0); setTimeout(()=>window.scrollTo(0,0),0); }
addEventListener('load', jumpToTop);
addEventListener('pageshow', e => { if (e.persisted) jumpToTop(); });

(() => {
  gsap.registerPlugin(ScrollTrigger);

  const scene = document.getElementById('half-arc-timeline');
  const pinEl = scene.querySelector('.pin');
  const ring  = scene.querySelector('.ring');
  const items = Array.from(scene.querySelectorAll('.ring-item'));
  const titleEl = scene.querySelector('.center-text .title');
  const descEl  = scene.querySelector('.center-text .desc');
  const btnEl   = scene.querySelector('.center-text .btn');

  const titles = ['Instant','Predictive','Accessible','Intelligent','Designed for you'];
  const descs  = [
    'What once took months, now happens in moments.',
    'So you never have to wait for symptoms.',
    'No longer limited by location or availability.',
    'Where data tells your story — and changes your outcome.',
    'Because you’re not a checkbox or a protocol.'
  ];
  const btns = [
    { text: 'Learn More',  href: '#instant'     },
    { text: 'See How',     href: '#predictive'  },
    { text: 'Get Started', href: '#accessible'  },
    { text: 'Discover',    href: '#intelligent' },
    { text: 'Try It Now',  href: '#designed'    }
  ];

  const N = items.length;
  const STEP = 360 / N;
  items.forEach((item, i) => item.style.setProperty('--deg', (STEP * i) + 'deg'));

  /* NEW: add one tiny dot per item (rotates with the spoke) */
  items.forEach((item) => {
    const d = document.createElement('span');
    d.className = 'ring-dot';
    item.appendChild(d);
  });

  let active = -1;
  function setActive(i){
    if (i === active) return;
    items[active]?.classList.remove('is-active');
    items[i]?.classList.add('is-active');

    titleEl.style.opacity = 0; 
    descEl.style.opacity  = 0;
    btnEl.style.opacity   = 0;

    setTimeout(() => {
      titleEl.textContent = titles[i] || '';
      descEl.textContent  = descs[i]  || '';
      btnEl.textContent   = btns[i]?.text || '';
      btnEl.href          = btns[i]?.href || '#';
      titleEl.style.opacity = 1; 
      descEl.style.opacity  = 1;
      btnEl.style.opacity   = 1;
    }, 140);

    active = i;
  }

  ring.style.setProperty('--rot', '-90deg');
  setActive(0);

  ScrollTrigger.create({
    trigger: scene,
    start: "top top",
    /*end: () => "+=" + (window.innerHeight * 1.25),*/
    end: () => "+=" + (window.innerHeight * 3),
    /*scrub: true,*/
     scrub: 1.2,
    pin: pinEl,
    pinSpacing: true,
    onUpdate: self => {
      const p = self.progress;
      const rot = -90 - p * ((N - 1) * STEP);
      ring.style.setProperty('--rot', rot + 'deg');
      const idx = Math.min(N - 1, Math.round(p * (N - 1)));
      setActive(idx);
    }
  });

  requestAnimationFrame(() => ScrollTrigger.refresh());
  setTimeout(() => ScrollTrigger.refresh(), 150);
})();
</script>
<script>
/* DROP-IN PATCH: normalize initial load if we start inside the scene */
(() => {
  const scene = document.getElementById('half-arc-timeline');
  if (!scene || !window.ScrollTrigger) return;

  const inScene = () => {
    const r = scene.getBoundingClientRect();
    const top = r.top + (pageYOffset || 0);
    const bottom = top + r.height;
    const y = pageYOffset || 0;
    return y > top - 2 && y < bottom + 2;
  };

  async function normalizeAtBoot() {
    try { await (document.fonts && document.fonts.ready); } catch(e){}
    requestAnimationFrame(() => {
      if (!inScene()) return;
      // Hide during normalization to avoid any blink/jump
      const prevVis = scene.style.visibility;
      scene.style.visibility = 'hidden';

      const r = scene.getBoundingClientRect();
      const top = r.top + (pageYOffset || 0);

      // Nudge scroll to just above trigger start so pinning measures correctly
      scrollTo(0, Math.max(0, top - 1));

      // After scroll, force a fresh measure
      requestAnimationFrame(() => {
        try { ScrollTrigger.refresh(true); } catch(e){}
        scene.style.visibility = prevVis;
      });
    });
  }

  if (document.readyState === 'complete') normalizeAtBoot();
  else addEventListener('load', normalizeAtBoot);
  // Handle Safari back/forward cache restores
  addEventListener('pageshow', e => { if (e.persisted) normalizeAtBoot(); });
})();
</script>