Skip to content
LAM
Read Home Blog
Make Projects HTML Tools Games
Touch grass Notes Resume Links
Home Blog HTML Projects
Tools Games Notes Resume Links
Back Rolling Edge Compute Vision Fun
Download Open
Show description 1,273 chars · Fun

Rolling Edge Compute Vision

Rolling Edge Compute Vision

autonomous rolling infrastructure

the car is not just transportation.it becomes a powered, connected, self-hosted ai work platform.

this concept turns a vehicle into a rolling edge compute node: power from the vehicle, internet from starlink or hotspot,
a local machine running agents and tools, a browser-based cockpit for control, and sync pipelines out to the public internet.
websites, proposals, lead capture, dashboards, local ai, deployment, backups, even field operations can all connect into one living system.

vehiclethe enclosure, mobility, climate, and baseline power source.

uplinkstarlink, hotspot, wi-fi, ethernet, or delayed sync whenever signal appears.

brainmini pc, mac mini, pi, or jetson handling local apps, agents, queues, and storage.

cockpitbrowser dashboard on tesla screen, phone, tablet, or laptop for control and review.

system map

this is the visual model: each node is a layer of the stack, and every labeled line shows a meaningful connection you can actually design around.

vehicle layer

tesla / ev platform

powered enclosure, mobility, cabin screen, accessory power, climate-stable mobile base.

power path

12v / low-voltage path

clean dc power strategy for router, compute, storage, and sensors.…

Rolling Edge Compute Vision

31,461 bytes · HTML source
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Rolling Edge Compute Vision</title>
  <style>
    :root {
      --bg: #060816;
      --bg2: #0b1024;
      --panel: rgba(16, 24, 48, 0.74);
      --panel-strong: rgba(13, 20, 40, 0.95);
      --line: rgba(116, 162, 255, 0.28);
      --line-strong: rgba(116, 162, 255, 0.78);
      --text: #eef3ff;
      --muted: #9bb0d7;
      --cyan: #79e8ff;
      --blue: #7aa7ff;
      --violet: #a990ff;
      --green: #7ff7b8;
      --gold: #ffd87c;
      --pink: #ff84d2;
      --shadow: 0 24px 80px rgba(0, 0, 0, 0.45);
      --radius: 24px;
      --radius-sm: 16px;
      --max: 1400px;
    }

    * { box-sizing: border-box; }
    html, body { margin: 0; padding: 0; background: radial-gradient(circle at top, #13214a 0%, var(--bg) 34%, #03050b 100%); color: var(--text); font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
    body { overflow-x: hidden; }

    .noise, .grid, .glow {
      position: fixed;
      inset: 0;
      pointer-events: none;
      z-index: 0;
    }

    .grid {
      background-image:
        linear-gradient(rgba(255,255,255,0.04) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255,255,255,0.04) 1px, transparent 1px);
      background-size: 40px 40px;
      mask-image: radial-gradient(circle at center, black 40%, transparent 90%);
      opacity: 0.35;
    }

    .glow {
      background:
        radial-gradient(circle at 15% 15%, rgba(121,232,255,0.22), transparent 26%),
        radial-gradient(circle at 85% 20%, rgba(169,144,255,0.16), transparent 24%),
        radial-gradient(circle at 50% 65%, rgba(127,247,184,0.08), transparent 28%),
        radial-gradient(circle at 70% 90%, rgba(255,132,210,0.10), transparent 20%);
      filter: blur(30px);
      opacity: 0.9;
    }

    .noise {
      background-image:
        radial-gradient(rgba(255,255,255,0.035) 1px, transparent 1px);
      background-size: 8px 8px;
      opacity: 0.25;
      mix-blend-mode: soft-light;
    }

    .wrap {
      position: relative;
      z-index: 1;
      max-width: var(--max);
      margin: 0 auto;
      padding: 32px 20px 80px;
    }

    .hero {
      position: relative;
      padding: 42px 28px 32px;
      background: linear-gradient(180deg, rgba(18,28,56,0.86), rgba(8,12,24,0.82));
      border: 1px solid rgba(132, 170, 255, 0.16);
      border-radius: 32px;
      box-shadow: var(--shadow);
      overflow: hidden;
      backdrop-filter: blur(20px);
    }

    .hero::before {
      content: "";
      position: absolute;
      inset: auto -10% -30% auto;
      width: 340px;
      height: 340px;
      border-radius: 50%;
      background: radial-gradient(circle, rgba(121,232,255,0.26), transparent 62%);
      filter: blur(20px);
    }

    .eyebrow {
      display: inline-flex;
      align-items: center;
      gap: 10px;
      color: var(--cyan);
      letter-spacing: 0.16em;
      text-transform: uppercase;
      font-size: 12px;
      font-weight: 700;
      margin-bottom: 16px;
    }

    .dot {
      width: 10px;
      height: 10px;
      border-radius: 50%;
      background: linear-gradient(135deg, var(--cyan), var(--violet));
      box-shadow: 0 0 18px rgba(121,232,255,0.7);
    }

    h1 {
      margin: 0;
      font-size: clamp(2.3rem, 5vw, 5rem);
      line-height: 0.98;
      letter-spacing: -0.04em;
      max-width: 900px;
    }

    .sub {
      margin-top: 20px;
      max-width: 900px;
      color: var(--muted);
      font-size: clamp(1rem, 1.45vw, 1.22rem);
      line-height: 1.7;
    }

    .stat-row {
      margin-top: 28px;
      display: grid;
      grid-template-columns: repeat(4, minmax(0, 1fr));
      gap: 14px;
    }

    .stat {
      background: rgba(255,255,255,0.04);
      border: 1px solid rgba(135, 165, 255, 0.12);
      border-radius: 18px;
      padding: 16px 16px 14px;
      min-height: 96px;
    }

    .stat strong {
      display: block;
      font-size: 1.25rem;
      margin-bottom: 6px;
    }

    .stat span {
      color: var(--muted);
      font-size: 0.95rem;
      line-height: 1.5;
    }

    .section {
      margin-top: 26px;
      background: linear-gradient(180deg, rgba(9,13,24,0.92), rgba(10,15,28,0.84));
      border: 1px solid rgba(123, 160, 255, 0.12);
      border-radius: var(--radius);
      box-shadow: var(--shadow);
      overflow: hidden;
      backdrop-filter: blur(16px);
    }

    .section-head {
      padding: 24px 24px 0;
    }

    .section h2 {
      margin: 0 0 10px;
      font-size: clamp(1.4rem, 2vw, 2.2rem);
      letter-spacing: -0.03em;
    }

    .section p.lead {
      margin: 0;
      color: var(--muted);
      line-height: 1.65;
      max-width: 900px;
    }

    .system-shell {
      padding: 20px 18px 26px;
    }

    .canvas {
      position: relative;
      min-height: 900px;
      border-radius: 28px;
      background:
        radial-gradient(circle at 50% 48%, rgba(121,232,255,0.08), transparent 22%),
        linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0.01));
      border: 1px solid rgba(123, 160, 255, 0.12);
      overflow: hidden;
    }

    .canvas::before {
      content: "";
      position: absolute;
      inset: 0;
      background-image:
        linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px);
      background-size: 48px 48px;
      opacity: 0.28;
      mask-image: radial-gradient(circle at center, black 45%, transparent 95%);
    }

    svg.links {
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
      overflow: visible;
      z-index: 1;
    }

    .node {
      position: absolute;
      left: var(--x, 0%);
      top: var(--y, 0%);
      z-index: 2;
      width: 220px;
      padding: 16px 16px 14px;
      border-radius: 22px;
      border: 1px solid rgba(140, 178, 255, 0.15);
      background: linear-gradient(180deg, rgba(17,24,45,0.95), rgba(11,16,31,0.92));
      box-shadow: 0 18px 44px rgba(0,0,0,0.34);
      backdrop-filter: blur(20px);
      transform: translate(-50%, -50%);
      transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
    }

    .node:hover {
      transform: translate(-50%, -50%) translateY(-4px);
      border-color: rgba(145, 208, 255, 0.45);
      box-shadow: 0 28px 70px rgba(0,0,0,0.42);
    }

    .node .badge {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      font-size: 11px;
      letter-spacing: 0.14em;
      text-transform: uppercase;
      color: var(--muted);
      margin-bottom: 12px;
      font-weight: 700;
    }

    .node .badge::before {
      content: "";
      width: 8px;
      height: 8px;
      border-radius: 999px;
      background: currentColor;
      box-shadow: 0 0 14px currentColor;
    }

    .node h3 {
      margin: 0 0 10px;
      font-size: 1.12rem;
      letter-spacing: -0.02em;
    }

    .node p {
      margin: 0;
      color: var(--muted);
      font-size: 0.93rem;
      line-height: 1.5;
    }

    .node.core { width: 260px; background: linear-gradient(180deg, rgba(17,34,62,0.98), rgba(9,19,34,0.96)); }
    .node.core .badge { color: var(--cyan); }
    .node.vehicle .badge { color: var(--blue); }
    .node.power .badge { color: var(--gold); }
    .node.network .badge { color: var(--green); }
    .node.software .badge { color: var(--violet); }
    .node.interface .badge { color: var(--pink); }
    .node.sync .badge { color: #97f1ff; }

    .label {
      position: absolute;
      left: var(--x, 0%);
      top: var(--y, 0%);
      z-index: 3;
      transform: translate(-50%, -50%);
      padding: 8px 12px;
      border-radius: 999px;
      background: rgba(11, 15, 28, 0.95);
      border: 1px solid rgba(132, 171, 255, 0.12);
      color: #d3ddf7;
      font-size: 0.78rem;
      letter-spacing: 0.04em;
      box-shadow: 0 10px 22px rgba(0,0,0,0.26);
      white-space: nowrap;
    }

    .legend {
      display: flex;
      flex-wrap: wrap;
      gap: 10px;
      padding: 18px 0 0;
    }

    .legend span {
      display: inline-flex;
      align-items: center;
      gap: 10px;
      font-size: 0.88rem;
      color: var(--muted);
      padding: 10px 14px;
      border-radius: 999px;
      background: rgba(255,255,255,0.04);
      border: 1px solid rgba(132,171,255,0.10);
    }

    .legend i {
      width: 11px;
      height: 11px;
      border-radius: 50%;
      display: inline-block;
      box-shadow: 0 0 16px currentColor;
    }

    .grid-3, .grid-2 {
      display: grid;
      gap: 16px;
      padding: 22px 24px 26px;
    }

    .grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }

    .card {
      padding: 18px;
      border-radius: 22px;
      background: linear-gradient(180deg, rgba(255,255,255,0.05), rgba(255,255,255,0.03));
      border: 1px solid rgba(133, 171, 255, 0.12);
      min-height: 170px;
    }

    .card .mini {
      color: var(--cyan);
      text-transform: uppercase;
      font-size: 0.72rem;
      letter-spacing: 0.14em;
      font-weight: 700;
      margin-bottom: 10px;
      display: inline-block;
    }

    .card h3 {
      margin: 0 0 10px;
      font-size: 1.15rem;
      letter-spacing: -0.02em;
    }

    .card p, .card li {
      color: var(--muted);
      line-height: 1.6;
      font-size: 0.95rem;
    }

    .card ul {
      margin: 12px 0 0;
      padding-left: 18px;
    }

    .flow {
      display: grid;
      grid-template-columns: repeat(4, minmax(0, 1fr));
      gap: 14px;
      padding: 22px 24px 28px;
    }

    .step {
      position: relative;
      padding: 18px 16px 16px;
      border-radius: 20px;
      background: linear-gradient(180deg, rgba(255,255,255,0.05), rgba(255,255,255,0.03));
      border: 1px solid rgba(133, 171, 255, 0.12);
    }

    .step .num {
      width: 34px;
      height: 34px;
      border-radius: 50%;
      display: grid;
      place-items: center;
      font-weight: 800;
      color: #04101b;
      background: linear-gradient(135deg, var(--cyan), #fff);
      margin-bottom: 12px;
      box-shadow: 0 0 24px rgba(121,232,255,0.5);
    }

    .step h4 {
      margin: 0 0 8px;
      font-size: 1rem;
    }

    .step p {
      margin: 0;
      color: var(--muted);
      line-height: 1.55;
      font-size: 0.92rem;
    }

    .timeline {
      padding: 20px 24px 28px;
      display: grid;
      gap: 16px;
    }

    .time-card {
      display: grid;
      grid-template-columns: 180px 1fr;
      gap: 18px;
      align-items: start;
      padding: 18px;
      border-radius: 22px;
      background: linear-gradient(180deg, rgba(255,255,255,0.05), rgba(255,255,255,0.03));
      border: 1px solid rgba(133,171,255,0.12);
    }

    .time-card .phase {
      font-size: 1rem;
      letter-spacing: 0.1em;
      text-transform: uppercase;
      color: var(--cyan);
      font-weight: 800;
    }

    .time-card h3 {
      margin: 0 0 10px;
      font-size: 1.2rem;
    }

    .time-card p {
      margin: 0;
      color: var(--muted);
      line-height: 1.65;
    }

    .footer-note {
      padding: 0 24px 26px;
      color: var(--muted);
      font-size: 0.95rem;
      line-height: 1.7;
    }

    .pill-row {
      display: flex;
      gap: 10px;
      flex-wrap: wrap;
      margin-top: 14px;
    }

    .pill {
      padding: 8px 12px;
      border-radius: 999px;
      font-size: 0.85rem;
      color: #d8e5ff;
      border: 1px solid rgba(133,171,255,0.14);
      background: rgba(255,255,255,0.04);
    }

    .accent {
      color: var(--cyan);
    }

    @media (max-width: 1180px) {
      .stat-row, .grid-3, .flow { grid-template-columns: repeat(2, minmax(0, 1fr)); }
      .canvas { min-height: 1240px; }
    }

    @media (max-width: 860px) {
      .wrap { padding: 18px 14px 60px; }
      .hero { padding: 24px 18px 20px; }
      .stat-row, .grid-3, .grid-2, .flow { grid-template-columns: 1fr; }
      .time-card { grid-template-columns: 1fr; }
      .canvas {
        min-height: 1700px;
      }
      .node { width: 82vw; max-width: 310px; }
      .node.core { width: 84vw; max-width: 330px; }
      .label { font-size: 0.7rem; }
    }
  </style>
</head>
<body>
  <div class="grid"></div>
  <div class="glow"></div>
  <div class="noise"></div>

  <main class="wrap">
    <section class="hero">
      <div class="eyebrow"><span class="dot"></span> autonomous rolling infrastructure</div>
      <h1>the car is not just transportation.<br>it becomes a powered, connected, self-hosted ai work platform.</h1>
      <p class="sub">
        this concept turns a vehicle into a <span class="accent">rolling edge compute node</span>: power from the vehicle, internet from starlink or hotspot,
        a local machine running agents and tools, a browser-based cockpit for control, and sync pipelines out to the public internet.
        websites, proposals, lead capture, dashboards, local ai, deployment, backups, even field operations can all connect into one living system.
      </p>

      <div class="stat-row">
        <div class="stat"><strong>vehicle</strong><span>the enclosure, mobility, climate, and baseline power source.</span></div>
        <div class="stat"><strong>uplink</strong><span>starlink, hotspot, wi-fi, ethernet, or delayed sync whenever signal appears.</span></div>
        <div class="stat"><strong>brain</strong><span>mini pc, mac mini, pi, or jetson handling local apps, agents, queues, and storage.</span></div>
        <div class="stat"><strong>cockpit</strong><span>browser dashboard on tesla screen, phone, tablet, or laptop for control and review.</span></div>
      </div>
    </section>

    <section class="section">
      <div class="section-head">
        <h2>system map</h2>
        <p class="lead">this is the visual model: each node is a layer of the stack, and every labeled line shows a meaningful connection you can actually design around.</p>
      </div>
      <div class="system-shell">
        <div class="canvas" id="canvas">
          <svg class="links" id="links" viewBox="0 0 1000 900" preserveAspectRatio="none">
            <defs>
              <linearGradient id="linkGrad" x1="0" y1="0" x2="1" y2="1">
                <stop offset="0%" stop-color="#79e8ff" stop-opacity="0.92"></stop>
                <stop offset="100%" stop-color="#a990ff" stop-opacity="0.88"></stop>
              </linearGradient>
              <filter id="softGlow">
                <feGaussianBlur stdDeviation="3.8" result="blur"></feGaussianBlur>
                <feMerge>
                  <feMergeNode in="blur"></feMergeNode>
                  <feMergeNode in="SourceGraphic"></feMergeNode>
                </feMerge>
              </filter>
            </defs>
          </svg>

          <div class="node vehicle" data-x="13" data-y="26" style="--x:13%; --y:26%;">
            <div class="badge">vehicle layer</div>
            <h3>tesla / ev platform</h3>
            <p>powered enclosure, mobility, cabin screen, accessory power, climate-stable mobile base.</p>
          </div>

          <div class="node power" data-x="18" data-y="57" style="--x:18%; --y:57%;">
            <div class="badge">power path</div>
            <h3>12v / low-voltage path</h3>
            <p>clean dc power strategy for router, compute, storage, and sensors. the invisible foundation.</p>
          </div>

          <div class="node network" data-x="18" data-y="84" style="--x:18%; --y:84%;">
            <div class="badge">network</div>
            <h3>starlink mini + hotspot</h3>
            <p>mobile uplink for cloud sync, remote access, api calls, backups, and live collaboration.</p>
          </div>

          <div class="node core" data-x="50" data-y="50" style="--x:50%; --y:50%;">
            <div class="badge">core brain</div>
            <h3>local compute node</h3>
            <p>mini pc, mac mini, jetson, or pi running agents, local ai, sqlite, file queues, deployment scripts, and project state.</p>
          </div>

          <div class="node software" data-x="50" data-y="20" style="--x:50%; --y:20%;">
            <div class="badge">automation layer</div>
            <h3>openclaw / agents / queue runner</h3>
            <p>receives tasks, builds sites, drafts proposals, transforms voice notes into work, and manages background jobs.</p>
          </div>

          <div class="node interface" data-x="82" data-y="24" style="--x:82%; --y:24%;">
            <div class="badge">control surface</div>
            <h3>browser cockpit</h3>
            <p>tesla browser, tablet, laptop, or phone hitting a local dashboard to approve work and steer the system.</p>
          </div>

          <div class="node sync" data-x="82" data-y="50" style="--x:82%; --y:50%;">
            <div class="badge">sync layer</div>
            <h3>public internet + cloud targets</h3>
            <p>github, bluehost, domains, email, sms, storage, analytics, and external APIs for the outside-facing layer.</p>
          </div>

          <div class="node software" data-x="82" data-y="80" style="--x:82%; --y:80%;">
            <div class="badge">business systems</div>
            <h3>crm / proposals / lead engine</h3>
            <p>client intake, invoices, bids, qr forms, local crm, content engine, and automated follow-up.</p>
          </div>

          <div class="node software" data-x="50" data-y="82" style="--x:50%; --y:82%;">
            <div class="badge">local ai</div>
            <h3>ollama / whisper / tools</h3>
            <p>voice-to-task, code generation, summaries, local inference, and privacy-first work even when offline.</p>
          </div>

          <div class="node interface" data-x="50" data-y="8" style="--x:50%; --y:8%;">
            <div class="badge">input stream</div>
            <h3>voice commands + notes</h3>
            <p>"make a site for a roofer" or "draft a capability statement" becomes machine-readable work items.</p>
          </div>

          <div class="label" data-x="31" data-y="36" style="--x:31%; --y:36%;">power + mount + climate</div>
          <div class="label" data-x="31" data-y="68" style="--x:31%; --y:68%;">uplink + sync window</div>
          <div class="label" data-x="50" data-y="35" style="--x:50%; --y:35%;">job orchestration</div>
          <div class="label" data-x="66" data-y="37" style="--x:66%; --y:37%;">review + approval</div>
          <div class="label" data-x="65" data-y="66" style="--x:65%; --y:66%;">deploy + monitor</div>
          <div class="label" data-x="50" data-y="66" style="--x:50%; --y:66%;">local-first workflow</div>
          <div class="label" data-x="66" data-y="83" style="--x:66%; --y:83%;">lead flow + ops</div>
          <div class="label" data-x="50" data-y="14" style="--x:50%; --y:14%;">voice to action</div>
          <div class="label" data-x="82" data-y="65" style="--x:82%; --y:65%;">published presence</div>
        </div>

        <div class="legend">
          <span><i style="color:#7aa7ff"></i> vehicle / hardware</span>
          <span><i style="color:#ffd87c"></i> power architecture</span>
          <span><i style="color:#7ff7b8"></i> connectivity</span>
          <span><i style="color:#a990ff"></i> software + automation</span>
          <span><i style="color:#ff84d2"></i> human interface</span>
          <span><i style="color:#79e8ff"></i> external sync</span>
        </div>
      </div>
    </section>

    <section class="section">
      <div class="section-head">
        <h2>what this machine can actually do</h2>
        <p class="lead">think of the car as a container for workflows, not just a place where a laptop happens to be sitting.</p>
      </div>
      <div class="grid-3">
        <article class="card">
          <span class="mini">use case 01</span>
          <h3>rolling website factory</h3>
          <p>the system watches a queue of client requests, generates assets, scaffolds apps, and prepares staging links while you move between meetings.</p>
          <ul>
            <li>single-file php or flask builds</li>
            <li>sqlite setup and admin panels</li>
            <li>logo drafts and content generation</li>
          </ul>
        </article>
        <article class="card">
          <span class="mini">use case 02</span>
          <h3>local ai office</h3>
          <p>speech, notes, code, proposals, and task routing stay local first, then sync outward when signal appears.</p>
          <ul>
            <li>ollama for local reasoning</li>
            <li>whisper for voice capture</li>
            <li>summaries, drafts, and replies</li>
          </ul>
        </article>
        <article class="card">
          <span class="mini">use case 03</span>
          <h3>field service command center</h3>
          <p>one dashboard for leads, documents, signatures, jobs, maps, forms, and inventory while you’re out in the real world.</p>
          <ul>
            <li>qr intake pages</li>
            <li>bid packet generation</li>
            <li>customer updates and invoicing</li>
          </ul>
        </article>
      </div>
    </section>

    <section class="section">
      <div class="section-head">
        <h2>how a task flows through the stack</h2>
        <p class="lead">one voice idea can ripple through local ai, automation, storage, deployment, and client-facing delivery.</p>
      </div>
      <div class="flow">
        <div class="step">
          <div class="num">1</div>
          <h4>capture</h4>
          <p>you speak or type a goal. the system turns it into a structured work order with tags, files, deadline, and expected outputs.</p>
        </div>
        <div class="step">
          <div class="num">2</div>
          <h4>orchestrate</h4>
          <p>agents route the work: design, code, copy, database, domain, forms, crm, or deployment. each subtask becomes a queue item.</p>
        </div>
        <div class="step">
          <div class="num">3</div>
          <h4>build locally</h4>
          <p>the box generates files, runs scripts, stores data in sqlite, and produces a preview on the local network even with bad connectivity.</p>
        </div>
        <div class="step">
          <div class="num">4</div>
          <h4>sync outward</h4>
          <p>when connectivity is available, the system pushes to github, hosting, email, backups, analytics, or external business platforms.</p>
        </div>
      </div>
    </section>

    <section class="section">
      <div class="section-head">
        <h2>now, next, and later</h2>
        <p class="lead">the real power is not the first version. it’s how the same architecture keeps expanding without changing the core idea.</p>
      </div>
      <div class="timeline">
        <article class="time-card">
          <div class="phase">now</div>
          <div>
            <h3>mobile self-hosted workstation</h3>
            <p>vehicle power, starlink or hotspot, mini computer, local web dashboard, local ai tools, and background automation. enough to run a business stack, manage sites, process client work, and sync out when needed.</p>
            <div class="pill-row">
              <span class="pill">local dashboard</span>
              <span class="pill">sqlite + files</span>
              <span class="pill">code generation</span>
              <span class="pill">cloud sync</span>
            </div>
          </div>
        </article>

        <article class="time-card">
          <div class="phase">next</div>
          <div>
            <h3>agentic business machine</h3>
            <p>it starts monitoring inboxes, turning requests into projects, drafting replies, generating proposals, updating the crm, and asking you for approval only at the moments that matter. less “toolbox,” more “teammate.”</p>
            <div class="pill-row">
              <span class="pill">email triage</span>
              <span class="pill">lead scoring</span>
              <span class="pill">proposal builder</span>
              <span class="pill">autonomous follow-up</span>
            </div>
          </div>
        </article>

        <article class="time-card">
          <div class="phase">later</div>
          <div>
            <h3>rolling edge cloud</h3>
            <p>multiple nodes with assigned roles: one for ai inference, one for rendering, one for backups, one for public hosting, one for sensor intake. each node can sync, specialize, and coordinate. now it’s not a car with a computer. it’s infrastructure with wheels.</p>
            <div class="pill-row">
              <span class="pill">fleet nodes</span>
              <span class="pill">distributed roles</span>
              <span class="pill">resilient sync mesh</span>
              <span class="pill">mobile private cloud</span>
            </div>
          </div>
        </article>
      </div>
    </section>

    <section class="section">
      <div class="section-head">
        <h2>design principles</h2>
        <p class="lead">these principles keep the concept grounded and actually buildable.</p>
      </div>
      <div class="grid-2">
        <article class="card">
          <span class="mini">principle</span>
          <h3>local-first wins</h3>
          <p>do not make connectivity a hard dependency. store jobs, files, logs, and previews locally first. treat internet as an optional accelerator and sync layer.</p>
        </article>
        <article class="card">
          <span class="mini">principle</span>
          <h3>browser is the cockpit</h3>
          <p>any screen becomes the interface. the real machine lives on the compute node; the display is only a control surface and review station.</p>
        </article>
        <article class="card">
          <span class="mini">principle</span>
          <h3>agents do background work</h3>
          <p>separate automated tasks from active review. the goal is passive throughput while moving, active approval when safely parked.</p>
        </article>
        <article class="card">
          <span class="mini">principle</span>
          <h3>clean power matters</h3>
          <p>power is architecture, not an afterthought. stable dc paths, thermal planning, mounting, and cable discipline are part of the system design.</p>
        </article>
      </div>
      <div class="footer-note">
        the deeper idea here is bigger than cars. this same pattern works in vans, trailers, mobile studios, field command rigs, maker vehicles, and pop-up business stations.
        once you can package compute + power + network + ai + browser control into one movable unit, you start designing <span class="accent">infrastructure that travels with you</span>.
      </div>
    </section>
  </main>

  <script>
    const canvas = document.getElementById('canvas');
    const svg = document.getElementById('links');

    const nodePairs = [
      [0, 3], [1, 3], [2, 3], [4, 3], [5, 3], [6, 3], [7, 3], [8, 3], [9, 4], [7, 6], [8, 6], [5, 6]
    ];

    function getCenter(el) {
      const rect = el.getBoundingClientRect();
      const base = canvas.getBoundingClientRect();
      return {
        x: rect.left - base.left + rect.width / 2,
        y: rect.top - base.top + rect.height / 2,
      };
    }

    function drawLinks() {
      const nodes = [...canvas.querySelectorAll('.node')];
      const width = canvas.clientWidth;
      const height = canvas.clientHeight;
      svg.setAttribute('viewBox', `0 0 ${width} ${height}`);
      svg.innerHTML = `
        <defs>
          <linearGradient id="linkGrad" x1="0" y1="0" x2="1" y2="1">
            <stop offset="0%" stop-color="#79e8ff" stop-opacity="0.92"></stop>
            <stop offset="100%" stop-color="#a990ff" stop-opacity="0.88"></stop>
          </linearGradient>
          <filter id="softGlow">
            <feGaussianBlur stdDeviation="3.8" result="blur"></feGaussianBlur>
            <feMerge>
              <feMergeNode in="blur"></feMergeNode>
              <feMergeNode in="SourceGraphic"></feMergeNode>
            </feMerge>
          </filter>
        </defs>`;

      nodePairs.forEach(([a, b], i) => {
        const p1 = getCenter(nodes[a]);
        const p2 = getCenter(nodes[b]);
        const cx1 = p1.x + (p2.x - p1.x) * 0.28;
        const cy1 = p1.y;
        const cx2 = p1.x + (p2.x - p1.x) * 0.72;
        const cy2 = p2.y;

        const glow = document.createElementNS('http://www.w3.org/2000/svg', 'path');
        glow.setAttribute('d', `M ${p1.x} ${p1.y} C ${cx1} ${cy1}, ${cx2} ${cy2}, ${p2.x} ${p2.y}`);
        glow.setAttribute('fill', 'none');
        glow.setAttribute('stroke', 'url(#linkGrad)');
        glow.setAttribute('stroke-opacity', '0.18');
        glow.setAttribute('stroke-width', '7');
        glow.setAttribute('filter', 'url(#softGlow)');
        svg.appendChild(glow);

        const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
        path.setAttribute('d', `M ${p1.x} ${p1.y} C ${cx1} ${cy1}, ${cx2} ${cy2}, ${p2.x} ${p2.y}`);
        path.setAttribute('fill', 'none');
        path.setAttribute('stroke', 'url(#linkGrad)');
        path.setAttribute('stroke-width', i < 8 ? '2.3' : '1.8');
        path.setAttribute('stroke-opacity', i < 8 ? '0.78' : '0.55');
        path.setAttribute('stroke-linecap', 'round');
        path.setAttribute('stroke-dasharray', i < 8 ? '6 8' : '3 8');
        path.style.animation = `dash ${5 + i * 0.35}s linear infinite`;
        svg.appendChild(path);
      });
    }

    function positionElements() {
      const isMobile = window.innerWidth <= 860;
      const nodes = [...canvas.querySelectorAll('.node')];
      const labels = [...canvas.querySelectorAll('.label')];

      if (isMobile) {
        const mobileNodes = [
          [50, 10], [50, 24], [50, 37], [50, 52], [50, 66],
          [50, 80], [50, 94], [50, 109], [50, 123], [50, 3]
        ];
        nodes.forEach((node, i) => {
          const [x, y] = mobileNodes[i];
          node.style.left = x + '%';
          node.style.top = y + '%';
        });

        const mobileLabels = [
          [74, 17], [74, 31], [74, 45], [74, 59], [74, 87],
          [26, 73], [26, 116], [26, 7], [75, 100]
        ];
        labels.forEach((label, i) => {
          const [x, y] = mobileLabels[i];
          label.style.left = x + '%';
          label.style.top = y + '%';
        });
        canvas.style.minHeight = '1700px';
      } else {
        nodes.forEach(node => {
          node.style.left = node.dataset.x + '%';
          node.style.top = node.dataset.y + '%';
        });
        labels.forEach(label => {
          label.style.left = label.dataset.x + '%';
          label.style.top = label.dataset.y + '%';
        });
        canvas.style.minHeight = window.innerWidth < 1180 ? '1240px' : '900px';
      }
      drawLinks();
    }

    const style = document.createElement('style');
    style.textContent = `
      @keyframes dash {
        from { stroke-dashoffset: 0; }
        to { stroke-dashoffset: -280; }
      }
    `;
    document.head.appendChild(style);

    window.addEventListener('resize', positionElements);
    window.addEventListener('load', positionElements);
    window.addEventListener('DOMContentLoaded', positionElements);
    requestAnimationFrame(positionElements);
    setTimeout(positionElements, 50);
    setTimeout(positionElements, 250);
  </script>
</body>
</html>