---
/**
 * Open-source section. Light, matching the bento's plinth-and-artifact
 * anatomy: copy and facts on the left, and on the right a tinted plinth
 * holding a repo browser (the real top-level tree) with a dark self-host
 * terminal floating over it. The dark band with clouds is gone; the terminal
 * is the one dark element, which is what makes it read as a terminal.
 */
const gh = '<path d="M12 .5C5.7.5.5 5.7.5 12c0 5.1 3.3 9.4 7.8 10.9.6.1.8-.2.8-.5v-2c-3.2.7-3.9-1.4-3.9-1.4-.5-1.3-1.3-1.7-1.3-1.7-1.1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1.1 1.8 2.8 1.3 3.5 1 .1-.8.4-1.3.8-1.6-2.6-.3-5.3-1.3-5.3-5.7 0-1.3.4-2.3 1.2-3.2-.1-.3-.5-1.5.1-3.1 0 0 1-.3 3.3 1.2 1-.3 2-.4 3-.4s2 .1 3 .4c2.3-1.5 3.3-1.2 3.3-1.2.7 1.6.2 2.9.1 3.1.8.8 1.2 1.9 1.2 3.2 0 4.4-2.7 5.4-5.3 5.7.4.4.8 1.1.8 2.2v3.3c0 .3.2.7.8.5 4.6-1.5 7.8-5.8 7.8-10.9C23.5 5.7 18.3.5 12 .5z"/>';
const arrow = '<path d="M7 17 17 7M7 7h10v10"/>';
const folder = '<path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/>';

// The actual top-level tree, with GitHub linguist colors on the dots.
const tree = [
  { path: 'cmd/backend', desc: 'API, the control plane', lang: 'Go', dot: '#00add8' },
  { path: 'cmd/worker', desc: 'Distributed senders', lang: 'Go', dot: '#00add8' },
  { path: 'tracking', desc: 'Open and click pixels', lang: 'Rust', dot: '#dea584' },
  { path: 'realtime', desc: 'Live dashboard fanout', lang: 'Elixir', dot: '#6e4a7e' },
  { path: 'web', desc: 'The dashboard', lang: 'TypeScript', dot: '#3178c6' },
  { path: 'docs', desc: 'docs.warmbly.com', lang: 'MDX', dot: '#fcb32c' },
];

const lines = [
  { kind: 'cmd', text: 'git clone https://github.com/warmbly/warmbly.git' },
  // cd is part of the command: without it make up runs in the parent
  // directory, finds no Makefile, and self-host startup fails.
  { kind: 'cmd', text: 'cd warmbly && make up' },
  { kind: 'ok', text: 'Warmbly is live on http://localhost:8080' },
  { kind: 'caret' },
];

const facts = [
  { value: '100%', label: 'Open source' },
  { value: '$0', label: 'To self-host' },
  { value: '1', label: 'Command to boot' },
  { value: 'Public', label: 'Issues and PRs' },
];

const links = [
  { label: 'Good first issues', href: 'https://github.com/warmbly/warmbly/labels/good%20first%20issue' },
  { label: 'Discussions', href: 'https://github.com/warmbly/warmbly/discussions' },
  { label: 'Architecture', href: '/trust/' },
  { label: 'Apache 2.0 license', href: 'https://github.com/warmbly/warmbly/blob/main/LICENSE' },
];
---
<section class="os container-page py-20 md:py-28">
  <div class="grid lg:grid-cols-12 gap-10 lg:gap-14 items-center">
    <!-- ── Copy ──────────────────────────────────────────────────── -->
    <div class="lg:col-span-5">
      <a
        href="https://github.com/warmbly/warmbly"
        target="_blank"
        rel="noopener noreferrer"
        class="inline-flex items-center gap-2 h-7 pl-2.5 pr-1.5 rounded-full bg-white ring-1 ring-[color:var(--border)] text-[12px] font-medium text-heading shadow-[0_2px_10px_-4px_rgba(15,23,42,0.2)] hover:ring-[color:var(--border-strong)] transition-colors"
      >
        <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor" set:html={gh}></svg>
        warmbly/warmbly
        <span class="inline-flex items-center h-[18px] px-1.5 rounded-full text-[9.5px] font-semibold uppercase tracking-[0.06em] bg-[color:var(--brand-soft)] text-[color:var(--brand-strong)]">Apache 2.0</span>
      </a>

      <h2 class="mt-5 text-[32px] md:text-[48px] font-semibold tracking-[-0.03em] leading-[1.04] text-heading">
        Read the code. Run it yourself.
      </h2>
      <p class="mt-4 text-foreground/75 leading-relaxed max-w-xl">
        See exactly how warmup, sending and deliverability work, self-host the whole platform on your own infra, and send a pull request when something should be better.
      </p>

      <div class="mt-7 flex flex-wrap items-center gap-3">
        <a
          href="https://github.com/warmbly/warmbly"
          target="_blank"
          rel="noopener noreferrer"
          class="inline-flex h-11 px-5 items-center gap-2 rounded-[10px] text-[14px] font-medium bg-foreground text-background hover:bg-foreground/85 transition-colors"
        >
          <svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor" class="shrink-0" set:html={gh}></svg>
          Star on GitHub
        </a>
        <a
          href="https://github.com/warmbly/warmbly/blob/main/CONTRIBUTING.md"
          target="_blank"
          rel="noopener noreferrer"
          class="inline-flex h-11 px-5 items-center rounded-[10px] text-[14px] font-medium text-heading bg-white ring-1 ring-[color:var(--border)] hover:ring-[color:var(--border-strong)] transition-colors"
        >
          Contributing guide
        </a>
      </div>

      <div class="mt-9 grid grid-cols-2 sm:grid-cols-4 gap-y-5">
        {facts.map((f, i) => (
          <div class:list={['pr-4', i > 0 && 'sm:border-l sm:border-[color:var(--border)] sm:pl-4']}>
            <div class="text-[22px] font-semibold tracking-[-0.02em] leading-none text-heading">{f.value}</div>
            <div class="mt-1.5 text-[10px] uppercase tracking-[0.14em] font-mono text-muted-foreground">{f.label}</div>
          </div>
        ))}
      </div>

      <div class="mt-8 flex flex-wrap items-center gap-x-5 gap-y-2">
        {links.map((l) => (
          <a
            href={l.href}
            target={l.href.startsWith('http') ? '_blank' : undefined}
            rel="noopener noreferrer"
            class="group inline-flex items-center gap-1 text-[12.5px] font-medium text-foreground/70 hover:text-heading transition-colors whitespace-nowrap"
          >
            {l.label}
            <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-muted-foreground/60 group-hover:text-heading transition-colors" set:html={arrow}></svg>
          </a>
        ))}
      </div>
    </div>

    <!-- ── Artifact: repo browser + floating terminal ────────────── -->
    <div class="lg:col-span-7">
      <div class="os-stage relative overflow-hidden h-[440px] rounded-[18px]"
           style="background: linear-gradient(175deg, #fbfcfe 0%, var(--surface-2) 100%); box-shadow: inset 0 0 0 1px var(--border);">
        <span class="os-texture absolute inset-0 pointer-events-none"></span>

        <!-- Back layer: the repository, dissolving toward the bottom. -->
        <div class="os-fade-b absolute left-7 right-7 top-7 bottom-0 rounded-t-[11px] bg-white ring-1 ring-slate-200/70 shadow-[0_10px_30px_-18px_rgb(15_23_42_/_0.25)]">
          <div class="h-10 px-4 border-b border-slate-200 flex items-center gap-2">
            <svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor" class="text-slate-500 shrink-0" set:html={gh}></svg>
            <span class="text-[12px] font-mono text-slate-900">warmbly<span class="text-slate-400">/</span>warmbly</span>
            <span class="inline-flex items-center gap-1 h-[19px] px-1.5 rounded-full text-[10px] font-mono bg-slate-100 text-slate-600 ring-1 ring-slate-200">
              <svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="6" x2="6" y1="3" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></svg>
              main
            </span>
            <span class="ml-auto text-[10px] font-mono text-slate-400">Go · Rust · Elixir · TS</span>
          </div>
          {tree.map((t, i) => (
            <div class="os-row flex items-center gap-2.5 px-4 py-[9px] border-b border-slate-200/60" style={`--i:${i}`}>
              <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" class="text-sky-600/80 shrink-0" set:html={folder}></svg>
              <span class="text-[12px] font-mono text-slate-900 whitespace-nowrap">{t.path}</span>
              <span class="text-[11.5px] text-slate-500 truncate">{t.desc}</span>
              <span class="ml-auto inline-flex items-center gap-1.5 text-[10.5px] font-mono text-slate-500 shrink-0">
                <span class="w-2 h-2 rounded-full" style={`background:${t.dot}`}></span>{t.lang}
              </span>
            </div>
          ))}
        </div>

        <!-- Front layer: the self-host terminal. -->
        <div class="os-term absolute left-12 sm:left-16 right-5 bottom-5 rounded-[11px] overflow-hidden bg-[#0b1120] ring-1 ring-white/10 shadow-[0_26px_60px_-18px_rgb(8_25_45_/_0.6)]">
          <div class="flex items-center gap-1.5 px-3.5 h-8 border-b border-white/[0.06] bg-white/[0.03]">
            <span class="w-2 h-2 rounded-full bg-[#ff5f57]"></span>
            <span class="w-2 h-2 rounded-full bg-[#febc2e]"></span>
            <span class="w-2 h-2 rounded-full bg-[#28c840]"></span>
            <span class="ml-2 text-[10px] font-mono text-white/35">self-host</span>
          </div>
          <div class="px-4 py-3 font-mono text-[12px] leading-[1.9]">
            {lines.map((l, i) => (
              <div class="os-line" style={`animation-delay: ${(i * 0.9).toFixed(1)}s`}>
                {l.kind === 'cmd' && (
                  <span><span class="text-sky-400">$</span><span class="text-white/90"> {l.text}</span></span>
                )}
                {l.kind === 'ok' && (
                  <span class="inline-flex items-center gap-2 text-emerald-300">
                    <span class="os-live w-1.5 h-1.5 rounded-full bg-emerald-400"></span>
                    <span>{l.text}</span>
                  </span>
                )}
                {l.kind === 'caret' && (
                  <span><span class="text-sky-400">$</span> <span class="os-caret inline-block w-[7px] h-[14px] -mb-0.5 bg-sky-300"></span></span>
                )}
              </div>
            ))}
          </div>
        </div>
      </div>

      <p class="mt-4 text-[12.5px] text-muted-foreground leading-relaxed">
        Self-hosted instances connect to the same shared warmup pool the cloud uses, the network of vetted mailboxes that builds your sender reputation. There is a free tier, so you can start warming on day one.
      </p>
    </div>
  </div>
</section>

<style>
  .os-texture {
    background-image: radial-gradient(circle, rgb(15 23 42 / 0.07) 1px, transparent 1.2px);
    background-size: 14px 14px;
    -webkit-mask-image: radial-gradient(ellipse at 50% 40%, #000 30%, transparent 78%);
    mask-image: radial-gradient(ellipse at 50% 40%, #000 30%, transparent 78%);
  }
  .os-fade-b {
    -webkit-mask-image: linear-gradient(to bottom, #000 55%, transparent 96%);
    mask-image: linear-gradient(to bottom, #000 55%, transparent 96%);
  }

  /* Motion is gated on .is-live, set by the observer below, so the loops
     stop when the section scrolls away. */
  @media (prefers-reduced-motion: no-preference) {
    .os.js-ready .os-row,
    .os.js-ready .os-term { opacity: 0; }
    .os.is-live .os-row {
      animation: os-rise 520ms cubic-bezier(0.22, 1, 0.36, 1) both;
      animation-delay: calc(var(--i) * 70ms);
    }
    .os.is-live .os-term {
      animation: os-drop 620ms cubic-bezier(0.34, 1.24, 0.64, 1) 380ms both;
    }
    /* Terminal lines type in one after another, then the loop rests. */
    .os.js-ready .os-line { opacity: 0; }
    .os.is-live .os-line { animation: os-line 9s ease-in-out infinite; }
    .os.is-live .os-caret { animation: os-caret 1s step-end infinite; }
    .os.is-live .os-live { animation: os-pulse 2.4s ease-in-out infinite; }
  }

  @keyframes os-rise {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
  }
  @keyframes os-drop {
    from { opacity: 0; transform: translateY(16px) scale(0.97); }
    to   { opacity: 1; transform: none; }
  }
  @keyframes os-line {
    0%, 8%    { opacity: 0; transform: translateY(3px); }
    13%, 88%  { opacity: 1; transform: none; }
    96%, 100% { opacity: 0; transform: translateY(3px); }
  }
  @keyframes os-caret { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }
  @keyframes os-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
</style>

<script is:inline>
  // Arm hidden start states only when JS runs, so a script failure leaves
  // the section fully visible.
  document.querySelectorAll('.os').forEach((el) => el.classList.add('js-ready'));
</script>

<script>
  // Same viewport gating as the bento, without pulling in a library for one
  // observer: loops run only while the section is on screen.
  const os = document.querySelector('.os');
  if (os) {
    new IntersectionObserver(
      (entries) => entries.forEach((e) => os.classList.toggle('is-live', e.isIntersecting)),
      { threshold: 0.2 },
    ).observe(os);
  }
</script>
