// Shared Blue North components: Logo, Nav, Footer, Topo, Reveal hook
const { useEffect, useRef, useState } = React;

// Brand mark — three overlapping rounded mountain peaks (Blue North logo)
function BrandMark({ size = 32 }) {
  return (
    <svg className="brand-mark" viewBox="0 0 56 32" width={size * 56 / 32} height={size} aria-hidden="true" fill="none">
      <g stroke="currentColor" strokeWidth="2" strokeLinejoin="round" strokeLinecap="round">
        {/* back-left peak (tallest) */}
        <path d="M6 25 L20 6 L34 25 Z" />
        {/* middle peak */}
        <path d="M18 25 L29 10 L40 25 Z" />
        {/* front-right peak (shortest) */}
        <path d="M30 25 L40 13 L50 25 Z" />
      </g>
    </svg>
  );
}

function Brand({ tag = true }) {
  return (
    <a href="index.html" className="brand" style={{ color: 'var(--ink)' }}>
      <BrandMark />
      <span>Blue North</span>
      {tag && <span className="brand-tag">CPA · Canada</span>}
    </a>
  );
}

function NavLive() {
  const [time, setTime] = useState('');
  useEffect(() => {
    const tick = () => {
      const d = new Date();
      const opts = { timeZone: 'America/Toronto', hour: '2-digit', minute: '2-digit', hour12: false };
      setTime(d.toLocaleTimeString('en-CA', opts));
    };
    tick();
    const id = setInterval(tick, 30000);
    return () => clearInterval(id);
  }, []);
  return (
    <span className="nav-live" aria-label="Studio status: online">
      <span className="nav-live-dot"></span>
      <span className="nav-live-text">YYZ {time || '--:--'}</span>
    </span>
  );
}

function Nav({ active }) {
  const [open, setOpen] = useState(false);
  useEffect(() => {
    if (open) document.body.style.overflow = 'hidden';
    else document.body.style.overflow = '';
    return () => { document.body.style.overflow = ''; };
  }, [open]);
  // Close on resize up to desktop
  useEffect(() => {
    const onR = () => { if (window.innerWidth > 820) setOpen(false); };
    window.addEventListener('resize', onR);
    return () => window.removeEventListener('resize', onR);
  }, []);
  return (
    <>
      <header className="nav">
        <div className="wrap nav-inner">
          <Brand />
          <NavLive />
          <nav className="nav-links">
            <a href="index.html" className={active === 'home' ? 'active' : ''}>Home</a>
            <a href="services.html" className={active === 'services' ? 'active' : ''}>Services</a>
            <a href="about.html" className={active === 'about' ? 'active' : ''}>About</a>
            <a href="contact.html" className={active === 'contact' ? 'active' : ''}>Contact</a>
            <a href="contact.html" className="btn nav-cta">
              Book a call <span className="arrow">↗</span>
            </a>
          </nav>
          <button
            className={`nav-burger ${open ? 'is-open' : ''}`}
            aria-label={open ? 'Close menu' : 'Open menu'}
            aria-expanded={open}
            onClick={() => setOpen(o => !o)}
          >
            <span></span><span></span><span></span>
          </button>
        </div>
      </header>
      {/* Drawer outside <header> so nav backdrop-filter doesn't bleed through the panel */}
      <div className={`nav-drawer ${open ? 'is-open' : ''}`} onClick={() => setOpen(false)}>
        <div className="nav-drawer-inner" onClick={(e) => e.stopPropagation()}>
          <div className="nav-drawer-meta">
            <span className="numlabel">Blue North · Menu</span>
          </div>
          <a href="index.html" className={active === 'home' ? 'active' : ''}>
            <span className="nav-drawer-num">01</span> Home
          </a>
          <a href="services.html" className={active === 'services' ? 'active' : ''}>
            <span className="nav-drawer-num">02</span> Services
          </a>
          <a href="about.html" className={active === 'about' ? 'active' : ''}>
            <span className="nav-drawer-num">03</span> About
          </a>
          <a href="contact.html" className={active === 'contact' ? 'active' : ''}>
            <span className="nav-drawer-num">04</span> Contact
          </a>
          <a href="contact.html" className="btn nav-drawer-cta">
            Book a call <span className="arrow">↗</span>
          </a>
          <div className="nav-drawer-foot">
            <span>YYZ · Toronto</span>
            <span>info@bluenorthaccounting.ca</span>
          </div>
        </div>
      </div>
    </>
  );
}

// SystemBar — thin live data strip beneath the nav, sitewide
function SystemBar() {
  const [now, setNow] = useState(new Date());
  useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 1000);
    return () => clearInterval(id);
  }, []);

  // Compute "last sync" — pretend we sync every 6 minutes; show how long ago
  const minutesIntoSlot = (now.getSeconds() + now.getMinutes() * 60) % 360; // 360s = 6min
  const lastSyncMin = Math.floor(minutesIntoSlot / 60);
  const lastSyncSec = minutesIntoSlot % 60;
  const lastSync = lastSyncMin > 0 ? `${lastSyncMin}m ${lastSyncSec}s ago` : `${lastSyncSec}s ago`;

  // Faux build / version (deterministic)
  const buildId = 'a7f3c2e';
  const version = 'v2.4.1';

  // Faux uptime — 99.98% (static, professional looking)
  const uptime = '99.98%';

  // CRA filing window indicator — simple month-aware
  const month = now.getMonth();
  const fiscalContext = month >= 0 && month <= 3 ? 'T1 SEASON · OPEN'
                      : month >= 4 && month <= 5 ? 'POST-FILING REVIEW'
                      : month >= 6 && month <= 8 ? 'PLANNING SEASON'
                      : 'YEAR-END PREP';

  return (
    <div className="sysbar" role="status" aria-label="Studio system status">
      <div className="wrap sysbar-inner">
        <div className="sysbar-group">
          <span className="sysbar-pill sysbar-live">
            <span className="sysbar-dot"></span>
            <span className="sysbar-k">Studio</span>
            <span className="sysbar-v">Online</span>
          </span>
          <span className="sysbar-pill">
            <span className="sysbar-k">Sync</span>
            <span className="sysbar-v sysbar-mono">{lastSync}</span>
          </span>
          <span className="sysbar-pill sysbar-hide-md">
            <span className="sysbar-k">Uptime · 90d</span>
            <span className="sysbar-v sysbar-mono">{uptime}</span>
          </span>
        </div>
        <div className="sysbar-group sysbar-right">
          <span className="sysbar-pill sysbar-hide-md">
            <span className="sysbar-k">Cycle</span>
            <span className="sysbar-v sysbar-mono">{fiscalContext}</span>
          </span>
          <span className="sysbar-pill sysbar-hide-sm">
            <span className="sysbar-k">Build</span>
            <span className="sysbar-v sysbar-mono">{version} · {buildId}</span>
          </span>
        </div>
      </div>
    </div>
  );
}

// Mountain silhouette motif — echoes the brand mark, replaces the topographic lines
function Topo({ opacity = 0.08, color }) {
  const stroke = color || 'currentColor';
  const motif = (typeof document !== 'undefined' && document.documentElement.getAttribute('data-motif')) || 'mountains';
  if (motif === 'none') return null;

  const [, force] = React.useState(0);
  React.useEffect(() => {
    const obs = new MutationObserver(() => force(n => n + 1));
    obs.observe(document.documentElement, { attributes: true, attributeFilter: ['data-motif'] });
    return () => obs.disconnect();
  }, []);

  if (motif === 'dots') {
    return (
      <div className="topo" aria-hidden="true">
        <svg viewBox="0 0 1600 900" preserveAspectRatio="xMidYMid slice">
          <defs>
            <pattern id="dotgrid" width="32" height="32" patternUnits="userSpaceOnUse">
              <circle cx="1" cy="1" r="1" fill={stroke} opacity={opacity * 0.7} />
            </pattern>
          </defs>
          <rect width="1600" height="900" fill="url(#dotgrid)" />
        </svg>
      </div>
    );
  }

  return (
    <div className="topo" aria-hidden="true">
      <svg viewBox="0 0 1600 900" preserveAspectRatio="xMidYMid slice">
        <defs>
          <pattern id="dotgrid2" width="32" height="32" patternUnits="userSpaceOnUse">
            <circle cx="1" cy="1" r="1" fill={stroke} opacity={opacity * 0.5} />
          </pattern>
        </defs>
        <rect width="1600" height="900" fill="url(#dotgrid2)" />
        <g fill={stroke} opacity={opacity * 1.4}>
          <path d="M 1100 900 L 1350 480 L 1600 900 Z" />
        </g>
        <g fill={stroke} opacity={opacity * 0.9}>
          <path d="M 1280 900 L 1450 580 L 1600 900 Z" />
        </g>
        <g fill={stroke} opacity={opacity * 0.6}>
          <path d="M 1380 900 L 1520 660 L 1600 900 Z" />
        </g>
      </svg>
    </div>
  );
}

function Footer() {
  return (
    <footer className="footer">
      <HeroFX />
      <div className="wrap">
        <div className="footer-grid">
          <div>
            <div className="footer-brand-line">
              <BrandMark size={28} />
              Blue North Accounting
            </div>
            <p className="footer-blurb">
              CPA-led accounting, advisory, and outsourced CFO services for Canadian
              small businesses — coast to coast, every province.
            </p>
            <div style={{ marginTop: 24, display: 'flex', gap: 12, flexWrap: 'wrap' }}>
              <span className="coord" style={{ color: 'var(--paper-2)', opacity: 0.7 }}>Serving all of Canada</span>
              <span className="coord" style={{ color: 'var(--paper-2)', opacity: 0.7 }}>·</span>
              <span className="coord" style={{ color: 'var(--paper-2)', opacity: 0.7 }}>10 provinces · 3 territories</span>
            </div>
          </div>
          <div>
            <h4>Services</h4>
            <ul>
              <li><a href="services.html">Bookkeeping</a></li>
              <li><a href="services.html">Tax & compliance</a></li>
              <li><a href="services.html">Outsourced CFO</a></li>
              <li><a href="services.html">Advisory</a></li>
            </ul>
          </div>
          <div>
            <h4>Firm</h4>
            <ul>
              <li><a href="about.html">About</a></li>
              <li><a href="about.html">Team</a></li>
              <li><a href="about.html">Approach</a></li>
              <li><a href="contact.html">Careers</a></li>
            </ul>
          </div>
          <div>
            <h4>Contact</h4>
            <ul>
              <li><a href="mailto:info@bluenorthaccounting.ca">info@bluenorthaccounting.ca</a></li>
              <li>Cloud-first<br />Serving all of Canada</li>
            </ul>
          </div>
        </div>
        <div className="footer-bottom">
          <span><span className="dot"></span> Proudly Canadian · Built for Canadian business</span>
          <span>© 2026 Blue North Accounting Inc.</span>
          <span>CPA-led · Canadian small business</span>
        </div>
      </div>
    </footer>
  );
}

// Interactive vector-network canvas — nodes drift, link to neighbours, and
// reach toward the cursor. Reused across every page hero. Pure vanilla canvas.
function HeroFX() {
  const ref = useRef(null);
  useEffect(() => {
    const canvas = ref.current;
    if (!canvas) return;
    const stage = canvas.parentElement;
    const ctx = canvas.getContext('2d');
    const reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    let w = 0, h = 0, dpr = 1, nodes = [], raf = 0;
    const mouse = { x: -9999, y: -9999 };

    function bd() {
      const r = stage.getBoundingClientRect();
      dpr = Math.min(window.devicePixelRatio || 1, 2);
      w = r.width; h = r.height;
      canvas.width = Math.max(1, Math.floor(w * dpr));
      canvas.height = Math.max(1, Math.floor(h * dpr));
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
      const target = Math.max(22, Math.min(72, Math.floor((w * h) / 15000)));
      nodes = Array.from({ length: target }, () => ({
        x: Math.random() * w, y: Math.random() * h,
        vx: (Math.random() - 0.5) * 0.32, vy: (Math.random() - 0.5) * 0.32,
        r: Math.random() * 1.5 + 0.7,
      }));
    }
    function frame() {
      ctx.clearRect(0, 0, w, h);
      for (const n of nodes) {
        n.x += n.vx; n.y += n.vy;
        if (n.x < 0 || n.x > w) n.vx *= -1;
        if (n.y < 0 || n.y > h) n.vy *= -1;
      }
      for (let i = 0; i < nodes.length; i++) {
        const a = nodes[i];
        const dm = Math.hypot(a.x - mouse.x, a.y - mouse.y);
        if (dm < 190) {
          const o = (1 - dm / 190) * 0.45;
          ctx.strokeStyle = 'rgba(120,150,255,' + o + ')';
          ctx.lineWidth = 0.8;
          ctx.beginPath(); ctx.moveTo(a.x, a.y); ctx.lineTo(mouse.x, mouse.y); ctx.stroke();
        }
        for (let j = i + 1; j < nodes.length; j++) {
          const b = nodes[j];
          const d = Math.hypot(a.x - b.x, a.y - b.y);
          if (d < 128) {
            const o = (1 - d / 128) * 0.2;
            ctx.strokeStyle = 'rgba(120,150,255,' + o + ')';
            ctx.lineWidth = 0.6;
            ctx.beginPath(); ctx.moveTo(a.x, a.y); ctx.lineTo(b.x, b.y); ctx.stroke();
          }
        }
      }
      for (const n of nodes) {
        const near = Math.hypot(n.x - mouse.x, n.y - mouse.y) < 190;
        ctx.beginPath();
        ctx.arc(n.x, n.y, near ? n.r + 1 : n.r, 0, Math.PI * 2);
        if (near) { ctx.shadowColor = 'rgba(90,130,255,0.9)'; ctx.shadowBlur = 8; ctx.fillStyle = 'rgba(160,185,255,0.95)'; }
        else { ctx.shadowBlur = 0; ctx.fillStyle = 'rgba(120,150,255,0.42)'; }
        ctx.fill();
      }
      ctx.shadowBlur = 0;
      if (!reduce) raf = requestAnimationFrame(frame);
    }
    bd(); frame();
    const onMove = (e) => { const r = stage.getBoundingClientRect(); mouse.x = e.clientX - r.left; mouse.y = e.clientY - r.top; };
    const onLeave = () => { mouse.x = -9999; mouse.y = -9999; };
    stage.addEventListener('pointermove', onMove);
    stage.addEventListener('pointerleave', onLeave);
    const ro = ('ResizeObserver' in window) ? new ResizeObserver(() => bd()) : null;
    if (ro) ro.observe(stage); else window.addEventListener('resize', bd);
    return () => {
      cancelAnimationFrame(raf);
      stage.removeEventListener('pointermove', onMove);
      stage.removeEventListener('pointerleave', onLeave);
      if (ro) ro.disconnect(); else window.removeEventListener('resize', bd);
    };
  }, []);
  return <canvas ref={ref} className="hero-fx" aria-hidden="true" />;
}

// Full dark-hero background layer stack (network + aurora + grid floor).
function HeroBG() {
  return (
    <>
      <HeroFX />
      <div className="hero-aurora-fx" aria-hidden="true">
        <span className="aur aur1"></span><span className="aur aur2"></span><span className="aur aur3"></span>
      </div>
      <div className="hero-gridfloor" aria-hidden="true"></div>
    </>
  );
}

// Magnetic pointer helpers for hero CTAs.
function heroMagnet(e) {
  const el = e.currentTarget, r = el.getBoundingClientRect();
  el.style.transform = 'translate(' + (e.clientX - (r.left + r.width / 2)) * 0.28 + 'px,' + (e.clientY - (r.top + r.height / 2)) * 0.4 + 'px)';
}
function heroDemagnet(e) { e.currentTarget.style.transform = ''; }

// StackStrip — "runs on the tools you already use" integration marquee.
// Signals a modern, connected, cloud-first firm without heavy assets.
function StackMark({ id }) {
  // Small, monochrome, currentColor glyphs — deliberately uniform, not brand logos.
  const P = { fill: 'none', stroke: 'currentColor', strokeWidth: 1.6, strokeLinecap: 'round', strokeLinejoin: 'round' };
  const marks = {
    xero: <circle cx="9" cy="9" r="6" {...P} />,
    qbo: <rect x="3" y="3" width="12" height="12" rx="6" {...P} />,
    stripe: <g {...P}><path d="M4 6.5c0-1 1.2-1.6 2.8-1.6 1.3 0 2.4.4 3.2.9" /><path d="M4.4 11.4c.9.6 2.1 1 3.4 1 1.7 0 2.8-.6 2.8-1.7 0-2.4-6-1.2-6-3.6" /></g>,
    shopify: <path d="M6 4.5 4 6l1 8 5 1 3-1-1-8-2-1.5z" {...P} />,
    dext: <g {...P}><path d="M5 3h5l3 3v9H5z" /><path d="M10 3v3h3" /></g>,
    plooto: <g {...P}><path d="M4 7h7l-2-2m2 2-2 2" /><path d="M14 11H7l2-2m-2 2 2 2" /></g>,
    wagepoint: <g {...P}><path d="M9 4v10" /><path d="M11.5 6c0-1-1-1.6-2.5-1.6S6.5 5.2 6.5 6.4 8 8 9 8.2s2.5.6 2.5 2-1.2 1.8-2.5 1.8-2.5-.6-2.5-1.6" /></g>,
    float: <path d="M3 10c1.5-2 3-2 4.5 0S10.5 12 12 10s3-2 3 0" {...P} />,
    fathom: <g {...P}><path d="M3 15V3" /><path d="M3 15h12" /><path d="M6 12l3-4 2 2 3-5" /></g>,
    cra: <path d="M9 3l1.4 3.2 3.4.3-2.6 2.2.9 3.3L9 12.4 5.9 14l.9-3.3L4.2 8.5l3.4-.3z" {...P} />,
  };
  return <svg viewBox="0 0 18 18" width="18" height="18" aria-hidden="true">{marks[id]}</svg>;
}

function StackStrip() {
  const TOOLS = [
    { id: 'xero', name: 'Xero' },
    { id: 'qbo', name: 'QuickBooks' },
    { id: 'stripe', name: 'Stripe' },
    { id: 'shopify', name: 'Shopify' },
    { id: 'dext', name: 'Dext' },
    { id: 'plooto', name: 'Plooto' },
    { id: 'wagepoint', name: 'Wagepoint' },
    { id: 'float', name: 'Float' },
    { id: 'fathom', name: 'Fathom' },
    { id: 'cra', name: 'CRA My Business' },
  ];
  const track = [...TOOLS, ...TOOLS]; // duplicate for seamless loop
  return (
    <section className="stack-strip" aria-label="Integrations — the tools we work with">
      <div className="wrap">
        <div className="stack-head">
          <span className="stack-live"><span className="stack-live-dot"></span>Connected</span>
          <span className="stack-label">Runs on the stack you already use — synced, not exported</span>
        </div>
      </div>
      <div className="stack-marquee" role="list">
        <div className="stack-track">
          {track.map((t, i) => (
            <span className="stack-item" role="listitem" key={i} aria-hidden={i >= TOOLS.length}>
              <StackMark id={t.id} />
              <span>{t.name}</span>
            </span>
          ))}
        </div>
      </div>
    </section>
  );
}

// Reveal-on-scroll
function useReveal() {
  useEffect(() => {
    const els = document.querySelectorAll('.reveal');
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting) {
          e.target.classList.add('in');
          io.unobserve(e.target);
        }
      });
    }, { threshold: 0.12 });
    els.forEach(el => io.observe(el));
    return () => io.disconnect();
  }, []);
}

// Tweaks defaults wrapper — each page applies these via TweaksHost
function applyTweaks(tweaks) {
  document.documentElement.setAttribute('data-palette', tweaks.palette || 'studio');
  document.documentElement.setAttribute('data-density', tweaks.density || 'comfortable');
}

Object.assign(window, {
  BrandMark, Brand, Nav, SystemBar, Topo, Footer, useReveal, applyTweaks, StackStrip,
  HeroFX, HeroBG, heroMagnet, heroDemagnet,
});
