/* =====================================================================
   Services — bento grid of build types + AI overlay.
   ===================================================================== */

const { useEffect: useEffectSv, useRef: useRefSv, useState: useStateSv } = React;

function Services() {
  const ref = useRefSv(null);
  useReveal(ref);
  return (
    <section id="services" ref={ref} className="mw-reveal" style={{ padding: 'clamp(64px, 10vh, 100px) clamp(20px, 4vw, 32px) 40px', maxWidth: 1440, margin: '0 auto' }}>
      <SectionHead
        eyebrow="001 / What we build"
        title={<>From a <em style={{ fontFamily: 'var(--font-editorial)', fontStyle: 'italic', fontWeight: 400, color: 'var(--accent)' }}>simple website</em><br/>to <em style={{ fontFamily: 'var(--font-editorial)', fontStyle: 'italic', fontWeight: 400 }}>everything you need</em>.</>}
        kicker="Every site is built by hand by our team — no AI slop, no templates. Lawn care, hydraulics, a food truck, a salon, whatever you do, we build the site that brings in business."
      />
      <ServicesGrid/>
    </section>
  );
}

function ServicesGrid() {
  const builds = [
    { icon: '▭',  name: 'Simple website',  ex: [
        'Lawn care · plumbers · contractors',
        'Auto · body shops · detailers',
        'Salons · barbers · spas',
        'Food trucks · caterers · cafés',
        'Personal trainers · coaches',
      ], budget: 'from $2k',   weeks: '3 days',  ai: '+chat' },
    { icon: '▦',  name: 'Listings site',   ex: [
        'Real estate · agents · brokers',
        'Used cars · dealerships',
        'Short-term rentals',
        'Farm equipment · RV lots',
        'Commercial property',
      ], budget: 'from $6k',   weeks: '2 wks',   ai: '+search' },
    { icon: '▣',  name: 'Full website',    ex: [
        'Multi-page · blog · gallery',
        'Law firms · medical practices',
        'Churches · nonprofits',
        'Construction · HVAC · roofing',
        'Consulting firms · agencies',
      ], budget: 'from $6k',   weeks: '3 wks',   ai: '+helper' },
    { icon: '▰',  name: 'Online store',    ex: [
        'Sell products · take orders · ship',
        'Boutique apparel · goods',
        'Local food · farm direct',
        'Custom merch · print on demand',
        'Digital downloads · subscriptions',
      ], budget: 'from $9k',   weeks: '4–6 wks', ai: '+support' },
    { icon: '★',  name: 'Google presence', ex: [
        'Local SEO · Google Business',
        'Review management',
        'Maps ranking · citations',
        'Content · blog posts',
        'Competitor monitoring',
      ], budget: 'from $1k',   weeks: 'ongoing', ai: 'add-on' },
    { icon: '◫',  name: 'Custom builds',   ex: [
        'Internal tools · dashboards',
        'Client portals · logins',
        'Data pipelines · integrations',
        'Platforms · marketplaces',
        'AI agents · automations',
      ], budget: 'custom',     weeks: '6+ wks',  ai: 'any AI' },
  ];
  return (
    <div className="mw-bento" style={{
      display: 'grid', gridTemplateColumns: 'repeat(12, 1fr)', gap: 8,
    }}>
      {/* Spectrum cell (wide) */}
      <div className="mw-bento-cell mw-bento-wide">
        <SpectrumCell/>
      </div>
      {/* AI banner */}
      <div className="mw-bento-cell mw-bento-ai">
        <AIBanner/>
      </div>

      {builds.map((b, i) => (
        <div key={b.name} className="mw-bento-cell mw-bento-build" style={{ '--i': i }}>
          <BuildCard {...b} seed={i}/>
        </div>
      ))}

      <div className="mw-bento-cell mw-bento-okc">
        <OKCMap/>
      </div>
      <div className="mw-bento-cell mw-bento-process">
        <ProcessCell/>
      </div>

    </div>
  );
}

function BuildCard({ icon, name, ex, budget, weeks, ai, seed = 0 }) {
  const [hover, setHover] = useStateSv(false);
  const examples = Array.isArray(ex) ? ex : [ex];
  const [idx, setIdx] = useStateSv(0);
  useEffectSv(() => {
    if (examples.length <= 1) return;
    // stagger each card slightly so they don't rotate in sync
    const t = setInterval(() => setIdx(i => (i + 1) % examples.length), 2200);
    const off = setTimeout(() => {}, seed * 380);
    return () => { clearInterval(t); clearTimeout(off); };
  }, []);

  return (
    <div
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
      style={{
        padding: 20, height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'space-between', gap: 20,
        background: 'var(--bg-elev-1)', border: '1px solid var(--border-strong)',
        borderColor: hover ? 'var(--accent)' : 'var(--border-strong)',
        transform: hover ? 'translateY(-2px)' : 'none',
        transition: 'transform .25s var(--ease-out), border-color .25s',
        cursor: 'pointer', position: 'relative', overflow: 'hidden',
    }}>
      {hover && <div style={{ position: 'absolute', inset: 0, background: 'radial-gradient(circle at 30% 0%, color-mix(in oklab, var(--accent) 12%, transparent), transparent 60%)', pointerEvents: 'none' }}/>}
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', position: 'relative' }}>
        <div style={{ fontFamily: 'var(--font-mono)', fontSize: 24, color: 'var(--accent)' }}>{icon}</div>
        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 4 }}>
          <div style={{ fontFamily: 'var(--font-mono)', fontSize: 9, letterSpacing: '0.16em', color: 'var(--fg-4)' }}>{budget}</div>
          <div style={{ fontFamily: 'var(--font-mono)', fontSize: 9, letterSpacing: '0.16em', color: 'var(--accent)' }}>{ai}</div>
        </div>
      </div>
      <div style={{ position: 'relative' }}>
        <div style={{ fontFamily: 'var(--font-display)', fontSize: 22, fontWeight: 500, letterSpacing: '-0.025em', color: 'var(--fg-1)' }}>{name}</div>
        <div style={{ fontSize: 12, color: 'var(--fg-3)', marginTop: 4, lineHeight: 1.45, position: 'relative', height: 18, overflow: 'hidden' }}>
          {examples.map((e, i) => (
            <div key={i} style={{
              position: 'absolute', inset: 0,
              transform: i === idx ? 'translateY(0)' : (i < idx ? 'translateY(-100%)' : 'translateY(100%)'),
              opacity: i === idx ? 1 : 0,
              transition: 'transform .5s var(--ease-out), opacity .5s var(--ease-out)',
            }}>{e}</div>
          ))}
        </div>
        {examples.length > 1 && (
          <div style={{ display: 'flex', gap: 3, marginTop: 8 }}>
            {examples.map((_, i) => (
              <div key={i} style={{
                width: i === idx ? 14 : 4, height: 2,
                background: i === idx ? 'var(--accent)' : 'var(--border-strong)',
                transition: 'width .4s var(--ease-out), background .4s',
              }}/>
            ))}
          </div>
        )}
        <div style={{ marginTop: 12, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--fg-4)', letterSpacing: '0.12em' }}>SHIPS IN {weeks}</div>
          <div style={{ fontFamily: 'var(--font-mono)', fontSize: 14, color: hover ? 'var(--accent)' : 'var(--fg-3)', transition: 'color .2s, transform .2s', transform: hover ? 'translateX(2px)' : 'none' }}>→</div>
        </div>
      </div>
    </div>
  );
}

function SpectrumCell() {
  const [pos, setPos] = useStateSv(35);
  const stages = [
    { x: 8,  label: 'SIMPLE',      note: '3 days' },
    { x: 28, label: 'LISTINGS',    note: '2 wks' },
    { x: 50, label: 'FULL SITE',   note: '3 wks' },
    { x: 72, label: 'STORE',       note: '4–6 wks' },
    { x: 92, label: 'CUSTOM',      note: '6+ wks' },
  ];
  const hovered = stages.reduce((best, s) => Math.abs(s.x - pos) < Math.abs(best.x - pos) ? s : best, stages[0]);
  return (
    <div
      onMouseMove={(e) => {
        const r = e.currentTarget.getBoundingClientRect();
        setPos(Math.max(4, Math.min(96, ((e.clientX - r.left) / r.width) * 100)));
      }}
      style={{ height: '100%', padding: 24, display: 'flex', flexDirection: 'column', justifyContent: 'space-between', position: 'relative', cursor: 'crosshair',
        background: 'var(--accent)', color: 'var(--accent-ink)', border: '1px solid var(--accent-strong)', minHeight: 200 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between' }}>
        <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, letterSpacing: '0.18em' }}>— COMPLEXITY SPECTRUM</div>
        <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, letterSpacing: '0.1em', opacity: .7 }}>↔ HOVER TO EXPLORE</div>
      </div>
      <div style={{ fontFamily: 'var(--font-display)', fontSize: 'clamp(36px, 5vw, 60px)', fontWeight: 500, lineHeight: 0.95, letterSpacing: '-0.04em' }}>
        {hovered.label}
        <span style={{ fontFamily: 'var(--font-editorial)', fontStyle: 'italic', fontWeight: 400, fontSize: 'clamp(20px, 2.8vw, 32px)', marginLeft: 12, opacity: 0.75 }}>— {hovered.note}</span>
      </div>
      <div style={{ position: 'relative', height: 40 }}>
        <div style={{ position: 'absolute', top: 18, left: 0, right: 0, height: 2, background: 'color-mix(in oklab, var(--accent-ink) 20%, transparent)' }}/>
        <div style={{ position: 'absolute', top: 18, left: 0, height: 2, background: 'var(--accent-ink)', width: `${pos}%`, transition: 'width .15s' }}/>
        {stages.map(s => (
          <div key={s.label} style={{ position: 'absolute', left: `${s.x}%`, top: 10, transform: 'translateX(-50%)', textAlign: 'center' }}>
            <div style={{ width: 2, height: 18, background: 'var(--accent-ink)' }}/>
            <div style={{ fontFamily: 'var(--font-mono)', fontSize: 8, letterSpacing: '0.1em', marginTop: 6, whiteSpace: 'nowrap', opacity: .7 }}>{s.label}</div>
          </div>
        ))}
        <div style={{ position: 'absolute', left: `${pos}%`, top: 8, transform: 'translateX(-50%)', width: 16, height: 16, background: 'var(--accent-ink)', boxShadow: '0 0 0 4px var(--accent)', transition: 'left .15s' }}/>
      </div>
      <div style={{ fontFamily: 'var(--font-mono)', fontSize: 9, letterSpacing: '0.14em', opacity: 0.65, display: 'flex', gap: 16, flexWrap: 'wrap' }}>
        <span>◆ ALL PLANS INCLUDE HOSTING</span>
        <span style={{ opacity: 0.5 }}>|</span>
        <span>MONTHLY PLANS AVAILABLE · FROM $79/MO</span>
        <span style={{ opacity: 0.5 }}>|</span>
        <span>NO LONG-TERM CONTRACT REQUIRED</span>
      </div>
    </div>
  );
}

function AIBanner() {
  // Multiple conversation examples — rotate through all of them
  const conversations = [
    [
      { r: 'user', t: 'is someone available saturday?' },
      { r: 'bot',  t: 'yes — 10am or 2pm open.' },
      { r: 'user', t: 'book 10am, john smith.' },
      { r: 'bot',  t: 'done. confirmation text sent.' },
    ],
    [
      { r: 'user', t: 'how much for a full detail?' },
      { r: 'bot',  t: 'interior + exterior starts at $180.' },
      { r: 'user', t: 'do you come to me?' },
      { r: 'bot',  t: 'yes — mobile service in OKC metro.' },
      { r: 'user', t: 'book me for friday.' },
      { r: 'bot',  t: 'friday 9am booked. you\'ll get a reminder.' },
    ],
    [
      { r: 'user', t: 'do you accept walk-ins?' },
      { r: 'bot',  t: 'yes, but appointments get priority.' },
      { r: 'user', t: 'what are your hours?' },
      { r: 'bot',  t: 'mon–sat 8am–6pm, closed sunday.' },
    ],
    [
      { r: 'user', t: 'can I see a menu?' },
      { r: 'bot',  t: 'sure — tap here for today\'s specials.' },
      { r: 'user', t: 'do you do catering?' },
      { r: 'bot',  t: 'yes, groups of 10+. want a quote?' },
      { r: 'user', t: 'yeah, 25 people.' },
      { r: 'bot',  t: 'got it. someone will call you today.' },
    ],
    [
      { r: 'user', t: 'is the 2018 f-150 still available?' },
      { r: 'bot',  t: 'yes — 47k miles, asking $28,900.' },
      { r: 'user', t: 'can I test drive tomorrow?' },
      { r: 'bot',  t: '10am works. name and number?' },
      { r: 'user', t: 'mike, 405-555-0192' },
      { r: 'bot',  t: 'see you at 10. address texted.' },
    ],
  ];

  const [convIdx, setConvIdx] = useStateSv(0);
  const [step, setStep] = useStateSv(0);

  useEffectSv(() => {
    const conv = conversations[convIdx];
    if (step < conv.length) {
      const t = setTimeout(() => setStep(s => s + 1), 1400);
      return () => clearTimeout(t);
    } else {
      // pause 2s then move to next conversation
      const t = setTimeout(() => {
        setConvIdx(i => (i + 1) % conversations.length);
        setStep(0);
      }, 2200);
      return () => clearTimeout(t);
    }
  }, [step, convIdx]);

  const msgs = conversations[convIdx].slice(0, step);

  return (
    <div style={{ padding: 22, height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'space-between', gap: 16, background: 'var(--bg-elev-2)', border: '1px solid var(--border-strong)', position: 'relative', overflow: 'hidden', minHeight: 200 }}>
      <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, letterSpacing: '0.18em', color: 'var(--fg-4)', display: 'flex', justifyContent: 'space-between' }}>
        <span>— AI FOR YOUR CUSTOMERS</span>
        <span style={{ color: 'var(--accent)', display: 'inline-flex', alignItems: 'center', gap: 6 }}>
          <DotTrace size={3}/>LIVE
        </span>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) minmax(0, 240px)', gap: 20, alignItems: 'start', flex: 1, minWidth: 0 }} className="mw-ai-banner-grid">
        <div style={{ minWidth: 0 }}>
          <div style={{ fontFamily: 'var(--font-display)', fontSize: 'clamp(26px, 3.2vw, 38px)', fontWeight: 500, lineHeight: 1, letterSpacing: '-0.03em' }}>We build by hand.<br/><span style={{ fontFamily: 'var(--font-editorial)', fontStyle: 'italic', fontWeight: 400, color: 'var(--accent)' }}>AI works for you.</span></div>
          <div style={{ fontSize: 12, color: 'var(--fg-3)', marginTop: 10, lineHeight: 1.4 }}>Real people design and code your site. Then — if you want — we plug in a smart helper that answers questions, books jobs, and fills out forms for your customers 24/7. Optional add-on from $400/mo.</div>
        </div>
        {/* Chat window — taller, bigger text, multi-conversation */}
        <div style={{ background: 'var(--bg)', border: '1px solid var(--border-strong)', display: 'flex', flexDirection: 'column', minWidth: 0, overflow: 'hidden' }}>
          {/* Chat header */}
          <div style={{ padding: '7px 10px', borderBottom: '1px solid var(--border)', display: 'flex', alignItems: 'center', gap: 7, background: 'var(--bg-elev-1)' }}>
            <span style={{ width: 6, height: 6, borderRadius: '50%', background: 'var(--accent)', boxShadow: '0 0 6px var(--accent)' }}/>
            <span style={{ fontFamily: 'var(--font-mono)', fontSize: 9, letterSpacing: '0.16em', color: 'var(--fg-4)' }}>SMART ASSISTANT · ONLINE</span>
          </div>
          {/* Messages */}
          <div style={{ padding: '10px 10px', display: 'flex', flexDirection: 'column', gap: 7, minHeight: 160, justifyContent: 'flex-end' }}>
            {msgs.map((m, i) => (
              <div key={`${convIdx}-${i}`} style={{
                padding: '6px 9px',
                fontFamily: 'var(--font-mono)', fontSize: 11, lineHeight: 1.4,
                color: m.r === 'user' ? 'var(--fg-2)' : 'var(--accent)',
                borderLeft: '2px solid ' + (m.r === 'user' ? 'var(--border-strong)' : 'var(--accent)'),
                background: m.r === 'bot' ? 'color-mix(in oklab, var(--accent) 5%, transparent)' : 'transparent',
                animation: 'mwFadeIn .3s',
                overflowWrap: 'anywhere', wordBreak: 'break-word',
              }}>{m.t}</div>
            ))}
            {/* typing indicator when waiting for bot reply */}
            {step < conversations[convIdx].length && conversations[convIdx][step]?.r === 'bot' && (
              <div style={{ padding: '6px 9px', borderLeft: '2px solid var(--accent)' }}>
                <DotTrace color="var(--accent)" size={4}/>
              </div>
            )}
          </div>
          {/* Conversation index dots */}
          <div style={{ display: 'flex', gap: 4, justifyContent: 'center', padding: '6px 10px', borderTop: '1px solid var(--border)' }}>
            {conversations.map((_, i) => (
              <div key={i} style={{ width: i === convIdx ? 12 : 4, height: 3, background: i === convIdx ? 'var(--accent)' : 'var(--border-strong)', transition: 'width .4s var(--ease-out), background .4s' }}/>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

function OKCMap() {
  return (
    <div style={{ padding: 22, height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'space-between', position: 'relative', overflow: 'hidden', background: 'var(--bg-elev-1)', border: '1px solid var(--border-strong)', minHeight: 180 }}>
      <svg viewBox="0 0 200 100" style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', opacity: 0.22 }}>
        {[...Array(10)].map((_, i) => (
          <path key={i} d={`M0 ${15 + i * 9} Q 50 ${10 + i * 9} 100 ${20 + i * 9} T 200 ${17 + i * 9}`}
            stroke="var(--accent)" strokeWidth="0.5" fill="none"/>
        ))}
        <circle cx="100" cy="55" r="3" fill="var(--accent)"/>
        <circle cx="100" cy="55" r="10" fill="none" stroke="var(--accent)" strokeWidth="0.5" strokeDasharray="2 2">
          <animate attributeName="r" values="6;16;6" dur="3s" repeatCount="indefinite"/>
          <animate attributeName="opacity" values="1;0;1" dur="3s" repeatCount="indefinite"/>
        </circle>
      </svg>
      <div style={{ position: 'relative', fontFamily: 'var(--font-mono)', fontSize: 10, letterSpacing: '0.18em', color: 'var(--fg-4)' }}>— HEADQUARTERS</div>
      <div style={{ position: 'relative' }}>
        <div style={{ fontFamily: 'var(--font-display)', fontSize: 'clamp(24px, 3vw, 32px)', fontWeight: 500, letterSpacing: '-0.03em', lineHeight: 1 }}>Oklahoma City</div>
        <div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, marginTop: 8, letterSpacing: '0.04em', color: 'var(--fg-3)' }}>35.4676°N · 97.5164°W</div>
        <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, marginTop: 4, letterSpacing: '0.14em', color: 'var(--accent)' }}>METRO + BEYOND</div>
      </div>
    </div>
  );
}

function ProcessCell() {
  const steps = [
    { n: '01', t: 'Consult', d: '30 min · free' },
    { n: '02', t: 'Scope',   d: '1–3 day write-up' },
    { n: '03', t: 'Build',   d: '2–12 weeks' },
    { n: '04', t: 'Ship',    d: 'deploy + handoff' },
  ];
  return (
    <div style={{ padding: 22, height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'space-between', background: 'var(--bg-elev-1)', border: '1px solid var(--border-strong)', gap: 16, minHeight: 180 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between' }}>
        <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, letterSpacing: '0.18em', color: 'var(--fg-4)' }}>— HOW IT WORKS</div>
        <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, letterSpacing: '0.14em', color: 'var(--accent)' }}>◆ 4 STEPS</div>
      </div>
      <div className="mw-process-steps" style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 14 }}>
        {steps.map(s => (
          <div key={s.n} style={{ borderLeft: '1px solid var(--border-strong)', paddingLeft: 12 }}>
            <div style={{ fontFamily: 'var(--font-mono)', fontSize: 9, color: 'var(--accent)', letterSpacing: '0.16em' }}>{s.n}</div>
            <div style={{ fontFamily: 'var(--font-display)', fontSize: 18, fontWeight: 500, letterSpacing: '-0.02em', marginTop: 6 }}>{s.t}</div>
            <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--fg-3)', marginTop: 2, letterSpacing: '0.04em' }}>{s.d}</div>
          </div>
        ))}
      </div>
    </div>
  );
}

/* =====================================================================
   SEO INTELLIGENCE — full-width cell with an animated OKC metro heat map.
   Real geography: Oklahoma County + Canadian + Cleveland + Logan county
   outlines, major interstates (I-35, I-40, I-44, I-240), the North
   Canadian & Canadian rivers, plotted from true lon/lat with a linear
   equirectangular projection centered on OKC.
   ===================================================================== */
function SEOIntelligenceCell() {
  const wrapRef = useRefSv(null);
  const canvasRef = useRefSv(null);
  const scrollRef = useRefSv(0);

  /* ===== Geographic data — real lon/lat pairs (WGS84-ish).
     Outlines are simplified polylines traced from USGS county/highway
     references. Precision is coarse on purpose — we want the map to
     READ as OKC metro, not survey it. */

  // Bounding window for the projection (lon min/max, lat min/max)
  const BOUNDS = { lonMin: -98.15, lonMax: -96.95, latMin: 34.95, latMax: 35.90 };

  // County outlines — each polygon is an array of [lon, lat] pairs.
  const COUNTIES = [
    // Oklahoma County (central, contains OKC downtown/Edmond/Midwest City/Del City)
    { name: 'Oklahoma', ring: [
      [-97.670, 35.663], [-97.140, 35.663], [-97.140, 35.400],
      [-97.670, 35.400], [-97.670, 35.663],
    ]},
    // Canadian County (west — Yukon, El Reno, Mustang partial)
    { name: 'Canadian', ring: [
      [-98.100, 35.663], [-97.670, 35.663], [-97.670, 35.400],
      [-98.100, 35.400], [-98.100, 35.663],
    ]},
    // Cleveland County (south — Norman, Moore, partial Midwest City)
    { name: 'Cleveland', ring: [
      [-97.670, 35.400], [-97.140, 35.400], [-97.140, 35.050],
      [-97.670, 35.050], [-97.670, 35.400],
    ]},
    // Logan County (north — Guthrie, Arcadia edge)
    { name: 'Logan', ring: [
      [-97.670, 35.840], [-97.140, 35.840], [-97.140, 35.663],
      [-97.670, 35.663], [-97.670, 35.840],
    ]},
    // Pottawatomie (east — Shawnee/McLoud)
    { name: 'Pottawatomie', ring: [
      [-97.140, 35.500], [-96.950, 35.500], [-96.950, 35.100],
      [-97.140, 35.100], [-97.140, 35.500],
    ]},
    // Grady (southwest corner — Tuttle/Chickasha edge)
    { name: 'Grady', ring: [
      [-98.100, 35.400], [-97.670, 35.400], [-97.670, 35.050],
      [-98.100, 35.050], [-98.100, 35.400],
    ]},
  ];

  // Interstates — each is a polyline. Paths follow the real corridors.
  const HIGHWAYS = [
    { name: 'I-35', line: [
      [-97.460, 35.870], [-97.475, 35.750], [-97.480, 35.670],
      [-97.485, 35.580], [-97.490, 35.480], [-97.490, 35.380],
      [-97.460, 35.260], [-97.445, 35.180], [-97.420, 35.060],
    ]},
    { name: 'I-40', line: [
      [-98.050, 35.520], [-97.950, 35.510], [-97.760, 35.490],
      [-97.600, 35.470], [-97.520, 35.465], [-97.440, 35.465],
      [-97.300, 35.475], [-97.200, 35.490], [-97.080, 35.490],
      [-96.950, 35.450],
    ]},
    { name: 'I-44', line: [
      [-98.100, 35.260], [-97.950, 35.350], [-97.800, 35.420],
      [-97.650, 35.460], [-97.550, 35.480], [-97.480, 35.495],
      [-97.440, 35.520], [-97.380, 35.580], [-97.300, 35.670],
      [-97.200, 35.750], [-97.080, 35.820],
    ]},
    { name: 'I-240', line: [
      [-97.640, 35.385], [-97.540, 35.380], [-97.440, 35.385],
      [-97.340, 35.395], [-97.250, 35.410],
    ]},
  ];

  // Rivers — North Canadian cuts east-west through OKC, Canadian river south
  const RIVERS = [
    { name: 'North Canadian', line: [
      [-98.080, 35.470], [-97.950, 35.495], [-97.800, 35.510],
      [-97.650, 35.500], [-97.530, 35.475], [-97.430, 35.445],
      [-97.320, 35.410], [-97.200, 35.380], [-97.080, 35.360],
      [-96.980, 35.330],
    ]},
    { name: 'Canadian', line: [
      [-98.100, 35.140], [-97.950, 35.170], [-97.800, 35.200],
      [-97.660, 35.210], [-97.500, 35.215], [-97.360, 35.220],
      [-97.200, 35.230], [-97.080, 35.235],
    ]},
  ];

  // City heat points — [lon, lat, weight, label]
  const points = useRefSv([
    { lon: -97.517, lat: 35.468, w: 1.00, t: 'Oklahoma City' },
    { lon: -97.478, lat: 35.653, w: 0.82, t: 'Edmond' },
    { lon: -97.397, lat: 35.449, w: 0.68, t: 'Midwest City' },
    { lon: -97.412, lat: 35.505, w: 0.54, t: 'Del City' },
    { lon: -97.488, lat: 35.339, w: 0.72, t: 'Moore' },
    { lon: -97.442, lat: 35.222, w: 0.78, t: 'Norman' },
    { lon: -97.762, lat: 35.506, w: 0.56, t: 'Yukon' },
    { lon: -97.836, lat: 35.620, w: 0.40, t: 'Piedmont' },
    { lon: -97.740, lat: 35.345, w: 0.48, t: 'Mustang' },
    { lon: -97.955, lat: 35.532, w: 0.42, t: 'El Reno' },
    { lon: -97.267, lat: 35.461, w: 0.46, t: 'Choctaw' },
    { lon: -97.093, lat: 35.349, w: 0.40, t: 'McLoud' },
    { lon: -97.325, lat: 35.712, w: 0.52, t: 'Arcadia' },
    { lon: -97.564, lat: 35.560, w: 0.44, t: 'The Village' },
    { lon: -97.670, lat: 35.290, w: 0.36, t: 'Tuttle' },
  ]).current;

  useEffectSv(() => {
    const wrap = wrapRef.current;
    if (!wrap) return;
    const onScroll = () => {
      const r = wrap.getBoundingClientRect();
      const vh = window.innerHeight || 1;
      const span = r.height + vh;
      const traveled = vh - r.top;
      scrollRef.current = Math.max(0, Math.min(1, traveled / span));
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
    };
  }, []);

  useEffectSv(() => {
    const canvas = canvasRef.current;
    const wrap = wrapRef.current;
    if (!canvas || !wrap) return;
    const ctx = canvas.getContext('2d');
    let raf, running = true, t0 = performance.now();

    const resize = () => {
      const dpr = Math.min(window.devicePixelRatio || 1, 2);
      const r = wrap.getBoundingClientRect();
      canvas.width = Math.max(1, Math.floor(r.width * dpr));
      canvas.height = Math.max(1, Math.floor(r.height * dpr));
      canvas.style.width = r.width + 'px';
      canvas.style.height = r.height + 'px';
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    };
    resize();
    const ro = new ResizeObserver(resize); ro.observe(wrap);

    const getAccent = () => {
      const cs = getComputedStyle(document.documentElement);
      return (cs.getPropertyValue('--accent').trim() || '#14b8a6');
    };
    const getFg = (v) => {
      const cs = getComputedStyle(document.documentElement);
      return (cs.getPropertyValue(v).trim() || '#aaa');
    };

    const draw = () => {
      if (!running) return;
      const r = wrap.getBoundingClientRect();
      const W = r.width, H = r.height;
      const t = (performance.now() - t0) * 0.001;
      const depth = scrollRef.current;
      ctx.clearRect(0, 0, W, H);

      const accent = getAccent();
      const fg3 = getFg('--fg-3');
      const fg4 = getFg('--fg-4');
      const border = getFg('--border-strong');

      // Compute a map viewport that preserves aspect ratio of the lon/lat
      // bounds while fitting the right 60% of the cell.
      const mapPadL = W * 0.38, mapPadT = H * 0.08;
      const availW = W * 0.60, availH = H * 0.84;
      const lonSpan = BOUNDS.lonMax - BOUNDS.lonMin;
      const latSpan = BOUNDS.latMax - BOUNDS.latMin;
      // Adjust for latitude compression (cos of mid-lat)
      const midLat = (BOUNDS.latMin + BOUNDS.latMax) / 2;
      const kx = Math.cos(midLat * Math.PI / 180);
      const geoW = lonSpan * kx;
      const geoH = latSpan;
      const scale = Math.min(availW / geoW, availH / geoH);
      const mapW = geoW * scale, mapH = geoH * scale;
      const mapLeft = mapPadL + (availW - mapW) / 2;
      const mapTop = mapPadT + (availH - mapH) / 2;

      const project = (lon, lat) => {
        const x = mapLeft + ((lon - BOUNDS.lonMin) * kx / geoW) * mapW;
        const y = mapTop + ((BOUNDS.latMax - lat) / geoH) * mapH;
        return [x, y];
      };

      // === Map frame / territory fill ===
      ctx.fillStyle = `color-mix(in oklab, ${accent} ${3 + depth * 4}%, transparent)`;
      ctx.fillRect(mapLeft, mapTop, mapW, mapH);
      ctx.strokeStyle = border;
      ctx.lineWidth = 1;
      ctx.globalAlpha = 0.5;
      ctx.strokeRect(mapLeft, mapTop, mapW, mapH);
      ctx.globalAlpha = 1;

      // Subtle lat/lon graticule
      ctx.strokeStyle = fg4;
      ctx.lineWidth = 0.5;
      ctx.globalAlpha = 0.12 + depth * 0.1;
      ctx.setLineDash([2, 4]);
      for (let lat = Math.ceil(BOUNDS.latMin * 4) / 4; lat < BOUNDS.latMax; lat += 0.25) {
        const [, y] = project(BOUNDS.lonMin, lat);
        ctx.beginPath(); ctx.moveTo(mapLeft, y); ctx.lineTo(mapLeft + mapW, y); ctx.stroke();
      }
      for (let lon = Math.ceil(BOUNDS.lonMin * 4) / 4; lon < BOUNDS.lonMax; lon += 0.25) {
        const [x] = project(lon, BOUNDS.latMin);
        ctx.beginPath(); ctx.moveTo(x, mapTop); ctx.lineTo(x, mapTop + mapH); ctx.stroke();
      }
      ctx.setLineDash([]);
      ctx.globalAlpha = 1;

      // === County outlines ===
      ctx.save();
      ctx.beginPath();
      ctx.rect(mapLeft, mapTop, mapW, mapH);
      ctx.clip();

      COUNTIES.forEach((cty) => {
        ctx.beginPath();
        cty.ring.forEach(([lon, lat], i) => {
          const [x, y] = project(lon, lat);
          if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
        });
        ctx.closePath();
        ctx.strokeStyle = fg3;
        ctx.lineWidth = 1;
        ctx.globalAlpha = 0.35 + depth * 0.25;
        ctx.stroke();
      });

      // County labels
      ctx.globalAlpha = 0.35 + depth * 0.3;
      ctx.fillStyle = fg4;
      ctx.font = '9px ui-monospace, SFMono-Regular, Menlo, monospace';
      ctx.textAlign = 'left';
      const labelFor = {
        Oklahoma:     [-97.500, 35.580],
        Canadian:     [-97.900, 35.580],
        Cleveland:    [-97.450, 35.280],
        Logan:        [-97.400, 35.760],
        Pottawatomie: [-97.050, 35.310],
        Grady:        [-97.900, 35.200],
      };
      COUNTIES.forEach(c => {
        const pos = labelFor[c.name];
        if (!pos) return;
        const [x, y] = project(pos[0], pos[1]);
        ctx.fillText(c.name.toUpperCase() + ' CO.', x, y);
      });
      ctx.globalAlpha = 1;

      // === Rivers ===
      RIVERS.forEach((riv) => {
        ctx.beginPath();
        riv.line.forEach(([lon, lat], i) => {
          const [x, y] = project(lon, lat);
          if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
        });
        ctx.strokeStyle = fg4;
        ctx.globalAlpha = 0.45;
        ctx.lineWidth = 1.2;
        ctx.stroke();
      });
      ctx.globalAlpha = 1;

      // === Highways ===
      HIGHWAYS.forEach((hw) => {
        ctx.beginPath();
        hw.line.forEach(([lon, lat], i) => {
          const [x, y] = project(lon, lat);
          if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
        });
        ctx.strokeStyle = fg3;
        ctx.globalAlpha = 0.55 + depth * 0.25;
        ctx.lineWidth = 1.4;
        ctx.stroke();

        // Highway shield label at midpoint of first long segment
        const mid = hw.line[Math.floor(hw.line.length / 2)];
        if (mid) {
          const [x, y] = project(mid[0], mid[1]);
          ctx.fillStyle = 'var(--bg)';
          ctx.globalAlpha = 1;
          // badge
          const badgeW = 26, badgeH = 12;
          ctx.fillStyle = getFg('--bg');
          ctx.fillRect(x - badgeW / 2, y - badgeH / 2, badgeW, badgeH);
          ctx.strokeStyle = fg3;
          ctx.lineWidth = 0.8;
          ctx.strokeRect(x - badgeW / 2, y - badgeH / 2, badgeW, badgeH);
          ctx.fillStyle = fg3;
          ctx.font = 'bold 8px ui-monospace, SFMono-Regular, Menlo, monospace';
          ctx.textAlign = 'center';
          ctx.textBaseline = 'middle';
          ctx.fillText(hw.name, x, y + 0.5);
          ctx.textBaseline = 'alphabetic';
          ctx.textAlign = 'left';
        }
      });
      ctx.globalAlpha = 1;

      // === Heat-point connection mesh (only at depth) ===
      if (depth > 0.08) {
        ctx.strokeStyle = accent;
        ctx.lineWidth = 0.6;
        for (let i = 0; i < points.length; i++) {
          for (let j = i + 1; j < points.length; j++) {
            const a = points[i], b = points[j];
            const [ax, ay] = project(a.lon, a.lat);
            const [bx, by] = project(b.lon, b.lat);
            const d = Math.hypot(ax - bx, ay - by);
            if (d < mapW * 0.18) {
              const pulse = 0.5 + 0.5 * Math.sin(t * 0.8 + (i + j));
              ctx.globalAlpha = Math.min(0.4, depth * 0.55) * pulse * (a.w + b.w) * 0.5;
              ctx.beginPath();
              ctx.moveTo(ax, ay); ctx.lineTo(bx, by);
              ctx.stroke();
            }
          }
        }
        ctx.globalAlpha = 1;
      }

      // === Heat blobs ===
      points.forEach((p, i) => {
        const [cx, cy] = project(p.lon, p.lat);
        const pulse = 0.55 + 0.45 * Math.sin(t * (1.1 + i * 0.17) + i);
        const intensity = p.w * (0.3 + depth * 0.8) * pulse;
        const rad = (16 + p.w * 32) * (0.6 + depth * 0.9);

        const g = ctx.createRadialGradient(cx, cy, 0, cx, cy, rad);
        g.addColorStop(0, `color-mix(in oklab, ${accent} ${60 * intensity}%, transparent)`);
        g.addColorStop(0.45, `color-mix(in oklab, ${accent} ${25 * intensity}%, transparent)`);
        g.addColorStop(1, 'transparent');
        ctx.fillStyle = g;
        ctx.beginPath(); ctx.arc(cx, cy, rad, 0, Math.PI * 2); ctx.fill();

        // core dot
        ctx.fillStyle = accent;
        ctx.globalAlpha = 0.65 + 0.35 * pulse;
        ctx.beginPath(); ctx.arc(cx, cy, 2 + p.w * 2, 0, Math.PI * 2); ctx.fill();
        ctx.globalAlpha = 1;

        // Ping rings on major cities
        if (p.w > 0.65) {
          const ringT = (t * 0.8 + i * 0.3) % 2;
          if (ringT < 1) {
            ctx.strokeStyle = accent;
            ctx.globalAlpha = (1 - ringT) * (0.45 + depth * 0.4);
            ctx.lineWidth = 1;
            ctx.beginPath();
            ctx.arc(cx, cy, 5 + ringT * 22, 0, Math.PI * 2);
            ctx.stroke();
            ctx.globalAlpha = 1;
          }
        }

        // City labels — only top-weight cities, labels fade in with depth
        if (p.w > 0.5 && depth > 0.15) {
          ctx.fillStyle = fg3;
          ctx.globalAlpha = Math.min(1, (depth - 0.15) * 2);
          ctx.font = '10px ui-monospace, SFMono-Regular, Menlo, monospace';
          ctx.textAlign = 'left';
          ctx.fillText(p.t.toUpperCase(), cx + 8, cy + 3);
          ctx.globalAlpha = 1;
        }
      });

      ctx.restore();

      // === Compass rose (top-right of map) ===
      const compX = mapLeft + mapW - 18, compY = mapTop + 18;
      ctx.strokeStyle = fg4;
      ctx.lineWidth = 0.8;
      ctx.globalAlpha = 0.6;
      ctx.beginPath();
      ctx.moveTo(compX, compY - 8); ctx.lineTo(compX, compY + 8);
      ctx.moveTo(compX - 8, compY); ctx.lineTo(compX + 8, compY);
      ctx.stroke();
      ctx.fillStyle = fg3;
      ctx.font = '8px ui-monospace, monospace';
      ctx.textAlign = 'center';
      ctx.fillText('N', compX, compY - 10);
      ctx.globalAlpha = 1;
      ctx.textAlign = 'left';

      raf = requestAnimationFrame(draw);
    };
    draw();
    return () => { running = false; cancelAnimationFrame(raf); ro.disconnect(); };
  }, []);

  return (
    <div ref={wrapRef} style={{
      position: 'relative', height: '100%', minHeight: 280,
      padding: 24, background: 'var(--bg-elev-1)',
      border: '1px solid var(--border-strong)', overflow: 'hidden',
      display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)', gap: 24,
    }}>
      {/* Canvas fills the cell, text sits on top-left */}
      <canvas ref={canvasRef} style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}/>

      {/* Left: copy */}
      <div style={{ position: 'relative', zIndex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'space-between', gap: 20, minWidth: 0 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between' }}>
          <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, letterSpacing: '0.18em', color: 'var(--fg-4)' }}>— SEO INTELLIGENCE</div>
          <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, letterSpacing: '0.14em', color: 'var(--accent)', display: 'inline-flex', alignItems: 'center', gap: 6 }}>
            <DotTrace size={3}/>LIVE · 15 NODES
          </div>
        </div>
        <div>
          <div style={{ fontFamily: 'var(--font-display)', fontSize: 'clamp(28px, 3.4vw, 44px)', fontWeight: 500, letterSpacing: '-0.035em', lineHeight: 0.98, color: 'var(--fg-1)' }}>
            Ranked where<br/>
            <span style={{ fontFamily: 'var(--font-editorial)', fontStyle: 'italic', fontWeight: 400, color: 'var(--accent)' }}>your customers</span> search.
          </div>
          <p style={{ fontSize: 13, color: 'var(--fg-3)', marginTop: 12, lineHeight: 1.5, maxWidth: 440 }}>
            We layer local intelligence over Google Business — matching searcher profiles, intent, and geography to the queries your neighborhood is actually typing. The map resolves deeper as you scroll: a live read on how people find you across the OKC metro.
          </p>
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 10, paddingTop: 12, borderTop: '1px solid var(--border-strong)' }}>
          {[
            { k: 'Queries / mo', v: '24.8K' },
            { k: 'Top-3 rank', v: '78%' },
            { k: 'Metros covered', v: '15' },
          ].map(s => (
            <div key={s.k}>
              <div style={{ fontFamily: 'var(--font-display)', fontSize: 22, fontWeight: 500, letterSpacing: '-0.02em', color: 'var(--fg-1)' }}>{s.v}</div>
              <div style={{ fontFamily: 'var(--font-mono)', fontSize: 9, letterSpacing: '0.16em', color: 'var(--fg-4)', textTransform: 'uppercase', marginTop: 2 }}>{s.k}</div>
            </div>
          ))}
        </div>
      </div>

      {/* Right: map surface (canvas draws here; this column holds labels) */}
      <div style={{ position: 'relative', zIndex: 1, minHeight: 220, minWidth: 0 }}>
        <div style={{ position: 'absolute', top: 0, right: 0, fontFamily: 'var(--font-mono)', fontSize: 9, letterSpacing: '0.16em', color: 'var(--fg-4)', textTransform: 'uppercase' }}>
          OKC Metro · 35.47°N 97.52°W
        </div>
        <div style={{ position: 'absolute', bottom: 0, right: 0, display: 'flex', alignItems: 'center', gap: 8, fontFamily: 'var(--font-mono)', fontSize: 9, letterSpacing: '0.14em', color: 'var(--fg-3)', textTransform: 'uppercase' }}>
          <span style={{ display: 'inline-block', width: 48, height: 4, background: 'linear-gradient(90deg, transparent, var(--accent))' }}/>
          Search intensity — scroll to deepen
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { Services });
