/* ============================================================
   Digital Odyssey, shared primitives (loaded on every page)
   Icons · Logo · Button · Badge · Container · Section
   Reveal hook · Stat counter · ThemeToggle · Orbit motif
   ============================================================ */

/* ---------- Icons (Lucide path data, ISC) ---------- */
const ICONS = {
  search:[['circle',{cx:11,cy:11,r:8}],['path',{d:'m21 21-4.3-4.3'}]],
  menu:[['path',{d:'M4 6h16'}],['path',{d:'M4 12h16'}],['path',{d:'M4 18h16'}]],
  x:[['path',{d:'M18 6 6 18'}],['path',{d:'M6 6l12 12'}]],
  'chevron-down':[['path',{d:'m6 9 6 6 6-6'}]],
  'chevron-right':[['path',{d:'m9 18 6-6-6-6'}]],
  'arrow-right':[['path',{d:'M5 12h14'}],['path',{d:'m12 5 7 7-7 7'}]],
  'arrow-up-right':[['path',{d:'M7 7h10v10'}],['path',{d:'M7 17 17 7'}]],
  'arrow-left':[['path',{d:'M19 12H5'}],['path',{d:'m12 19-7-7 7-7'}]],
  check:[['path',{d:'M20 6 9 17l-5-5'}]],
  'check-circle':[['path',{d:'M21.8 10A10 10 0 1 1 17 3.3'}],['path',{d:'m9 11 3 3L22 4'}]],
  plus:[['path',{d:'M5 12h14'}],['path',{d:'M12 5v14'}]],
  minus:[['path',{d:'M5 12h14'}]],
  star:[['path',{d:'M11.5 2.6a.6.6 0 0 1 1 0l2.4 5a.6.6 0 0 0 .46.33l5.4.78a.6.6 0 0 1 .33 1l-3.9 3.8a.6.6 0 0 0-.18.54l.92 5.37a.6.6 0 0 1-.87.63l-4.8-2.53a.6.6 0 0 0-.56 0l-4.8 2.53a.6.6 0 0 1-.87-.63l.92-5.37a.6.6 0 0 0-.18-.54l-3.9-3.8a.6.6 0 0 1 .33-1l5.4-.78a.6.6 0 0 0 .46-.33z'}]],
  quote:[['path',{d:'M3 21c3 0 7-1 7-8V5c0-1.25-.76-2-2-2H4c-1.24 0-2 .75-2 2v4c0 1.25.76 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z'}],['path',{d:'M15 21c3 0 7-1 7-8V5c0-1.25-.76-2-2-2h-4c-1.24 0-2 .75-2 2v4c0 1.25.76 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z'}]],
  megaphone:[['path',{d:'m3 11 18-5v12L3 14v-3z'}],['path',{d:'M11.6 16.8a3 3 0 1 1-5.8-1.6'}]],
  target:[['circle',{cx:12,cy:12,r:10}],['circle',{cx:12,cy:12,r:6}],['circle',{cx:12,cy:12,r:2}]],
  'pen-tool':[['path',{d:'M15.707 21.293a1 1 0 0 1-1.414 0l-1.586-1.586a1 1 0 0 1 0-1.414l5.586-5.586a1 1 0 0 1 1.414 0l1.586 1.586a1 1 0 0 1 0 1.414z'}],['path',{d:'m18 13-1.375-6.874a1 1 0 0 0-.746-.776L3.235 2.028a1 1 0 0 0-1.207 1.207L5.35 15.879a1 1 0 0 0 .776.746L13 18'}],['path',{d:'m2.3 2.3 7.286 7.286'}],['circle',{cx:11,cy:11,r:2}]],
  'bar-chart':[['path',{d:'M3 3v16a2 2 0 0 0 2 2h16'}],['path',{d:'M7 16V11'}],['path',{d:'M12 16V8'}],['path',{d:'M17 16v-3'}]],
  'trending-up':[['path',{d:'M16 7h6v6'}],['path',{d:'m22 7-8.5 8.5-5-5L2 17'}]],
  users:[['path',{d:'M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2'}],['circle',{cx:9,cy:7,r:4}],['path',{d:'M22 21v-2a4 4 0 0 0-3-3.87'}],['path',{d:'M16 3.13a4 4 0 0 1 0 7.75'}]],
  mail:[['rect',{width:20,height:16,x:2,y:4,rx:2}],['path',{d:'m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7'}]],
  phone:[['path',{d:'M13.832 16.568a1 1 0 0 0 1.213-.303l.355-.465A2 2 0 0 1 17 15h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2A18 18 0 0 1 2 4a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-.8 1.6l-.468.351a1 1 0 0 0-.292 1.233 14 14 0 0 0 6.392 6.384'}]],
  'map-pin':[['path',{d:'M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0'}],['circle',{cx:12,cy:10,r:3}]],
  calendar:[['path',{d:'M8 2v4'}],['path',{d:'M16 2v4'}],['rect',{width:18,height:18,x:3,y:4,rx:2}],['path',{d:'M3 10h18'}]],
  clock:[['circle',{cx:12,cy:12,r:10}],['path',{d:'M12 6v6l4 2'}]],
  zap:[['path',{d:'M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z'}]],
  sparkles:[['path',{d:'M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.962 0z'}],['path',{d:'M20 3v4'}],['path',{d:'M22 5h-4'}],['path',{d:'M4 17v2'}],['path',{d:'M5 18H3'}]],
  compass:[['circle',{cx:12,cy:12,r:10}],['polygon',{points:'16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76'}]],
  rocket:[['path',{d:'M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91 0z'}],['path',{d:'m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z'}],['path',{d:'M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0'}],['path',{d:'M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5'}]],
  layers:[['path',{d:'M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z'}],['path',{d:'m22 17.65-9.17 4.16a2 2 0 0 1-1.66 0L2 17.65'}],['path',{d:'m22 12.65-9.17 4.16a2 2 0 0 1-1.66 0L2 12.65'}]],
  globe:[['circle',{cx:12,cy:12,r:10}],['path',{d:'M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20'}],['path',{d:'M2 12h20'}]],
  heart:[['path',{d:'M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z'}]],
  shield:[['path',{d:'M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z'}]],
  eye:[['path',{d:'M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0'}],['circle',{cx:12,cy:12,r:3}]],
  sun:[['circle',{cx:12,cy:12,r:4}],['path',{d:'M12 2v2'}],['path',{d:'M12 20v2'}],['path',{d:'m4.93 4.93 1.41 1.41'}],['path',{d:'m17.66 17.66 1.41 1.41'}],['path',{d:'M2 12h2'}],['path',{d:'M20 12h2'}],['path',{d:'m6.34 17.66-1.41 1.41'}],['path',{d:'m19.07 4.93-1.41 1.41'}]],
  moon:[['path',{d:'M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9'}]],
  play:[['polygon',{points:'6 3 20 12 6 21 6 3'}]],
  instagram:[['rect',{width:20,height:20,x:2,y:2,rx:5,ry:5}],['path',{d:'M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z'}],['path',{d:'M17.5 6.5h.01'}]],
  linkedin:[['path',{d:'M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z'}],['rect',{width:4,height:12,x:2,y:9}],['circle',{cx:4,cy:4,r:2}]],
  twitter:[['path',{d:'M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z'}]],
  facebook:[['path',{d:'M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z'}]],
  dribbble:[['circle',{cx:12,cy:12,r:10}],['path',{d:'M19.13 5.09C15.22 9.14 10 10.44 2.25 10.94'}],['path',{d:'M21.75 12.84c-6.62-1.41-12.14 1-16.38 6.32'}],['path',{d:'M8.56 2.75c4.37 6 6 9.42 8 17.72'}]],
  behance:[['path',{d:'M7.5 6H2v12h5.7c2.4 0 4.1-1.2 4.1-3.4 0-1.5-.9-2.5-2.2-2.8 1-.4 1.7-1.3 1.7-2.5C11.3 7.1 9.8 6 7.5 6z'}],['path',{d:'M15 9h6'}],['path',{d:'M22 14c0-2-1.4-3.5-3.5-3.5S15 12 15 14.2s1.5 3.8 3.6 3.8c1.7 0 2.9-.8 3.3-2'}]],
  filter:[['polygon',{points:'22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3'}]],
  image:[['rect',{width:18,height:18,x:3,y:3,rx:2,ry:2}],['circle',{cx:9,cy:9,r:2}],['path',{d:'m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21'}]],
  film:[['rect',{width:18,height:18,x:3,y:3,rx:2}],['path',{d:'M7 3v18'}],['path',{d:'M3 7.5h4'}],['path',{d:'M3 12h18'}],['path',{d:'M3 16.5h4'}],['path',{d:'M17 3v18'}],['path',{d:'M17 7.5h4'}],['path',{d:'M17 16.5h4'}]],
};

function Icon({ name, size = 20, color = 'currentColor', stroke = 1.75, fill = 'none', style = {} }) {
  const kids = ICONS[name] || [];
  const solid = ['twitter','facebook','instagram'].includes(name) && fill === 'currentColor';
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill={fill}
      stroke={color} strokeWidth={fill === 'currentColor' && (name==='twitter'||name==='facebook') ? 0 : stroke}
      strokeLinecap="round" strokeLinejoin="round"
      style={{ display:'inline-block', flexShrink:0, ...style }} aria-hidden="true">
      {kids.map(([tag, attrs], i) => React.createElement(tag, { key:i, ...attrs }))}
    </svg>
  );
}

/* ---------- Orbit motif (brand device) ---------- */
function Orbit({ size = 40, spin = false, id = 'g' }) {
  const gid = 'orb-' + id;
  return (
    <svg width={size} height={size} viewBox="0 0 48 48" fill="none"
      style={{ display:'block', animation: spin ? 'do-spin 18s linear infinite' : 'none' }}>
      <defs>
        <linearGradient id={gid} x1="0" y1="0" x2="48" y2="48" gradientUnits="userSpaceOnUse">
          <stop stopColor="#1ae0e6" /><stop offset="1" stopColor="#0052ff" />
        </linearGradient>
      </defs>
      <circle cx="24" cy="24" r="7" fill={`url(#${gid})`} />
      <ellipse cx="24" cy="24" rx="20" ry="9" stroke={`url(#${gid})`} strokeWidth="2.5"
        transform="rotate(-32 24 24)" opacity="0.95" />
      <circle cx="40.5" cy="14.5" r="3" fill="#1ae0e6" />
    </svg>
  );
}

/* ---------- Logo (brand mark on a white badge + wordmark) ---------- */
function Logo({ size = 22, onClick }) {
  const d = `calc(${size*1.7}px * var(--logo-scale, 1))`;
  const pad = `calc(${size*0.18}px * var(--logo-scale, 1))`;
  return (
    <a href="index.html" onClick={onClick} style={{ display:'flex', alignItems:'center', gap:11 }}>
      <span style={{ display:'inline-flex', alignItems:'center', justifyContent:'center', background:'#fff', borderRadius:'9999px', padding:pad, lineHeight:0, boxShadow:'0 2px 10px rgba(0,0,0,.18)', flexShrink:0 }}>
        <img src="assets/logo.png" alt="Digital Odyssey" style={{ display:'block', width:d, height:d }} />
      </span>
      <span style={{ fontFamily:'var(--font-display)', fontWeight:600, fontSize:size, letterSpacing:'-0.6px', color:'var(--text)', whiteSpace:'nowrap', lineHeight:1 }}>
        Digital<span style={{ fontWeight:300 }}> Odyssey</span>
      </span>
    </a>
  );
}

/* ---------- Button ----------
   variant: primary | cyan | outline | ghost | dark | light
   size: md | lg | sm  */
function Button({ children, variant='primary', size='md', full=false, href, onClick, icon, style={} }) {
  const [hover, setHover] = React.useState(false);
  const base = {
    fontFamily:'var(--font-sans)', fontWeight:600, border:'none', cursor:'pointer',
    borderRadius:'var(--radius-pill)', display:'inline-flex', alignItems:'center', justifyContent:'center',
    gap:9, whiteSpace:'nowrap', textDecoration:'none', width: full ? '100%' : 'auto',
    transition:'transform .18s ease, box-shadow .18s ease, background .15s ease, opacity .15s ease, border-color .15s ease',
  };
  const sizes = {
    sm:{ height:40, padding:'0 18px', fontSize:14 },
    md:{ height:48, padding:'0 24px', fontSize:15 },
    lg:{ height:56, padding:'0 34px', fontSize:16 },
  }[size];
  const variants = {
    primary:{ background:'var(--primary)', color:'#fff', boxShadow: hover ? '0 12px 30px rgba(0,82,255,.4)':'0 6px 18px rgba(0,82,255,.22)' },
    cyan:{ background:'var(--grad-energy)', color:'#04122b', fontWeight:700, boxShadow: hover ? '0 12px 34px rgba(26,224,230,.4)':'0 6px 20px rgba(26,224,230,.2)' },
    outline:{ background:'transparent', color:'var(--text)', border:'1.5px solid var(--border)' },
    'outline-cyan':{ background:'transparent', color:'var(--text)', border:'1.5px solid', borderColor: hover ? 'var(--cyan)':'var(--border)' },
    dark:{ background:'var(--navy-elevated)', color:'#fff' },
    light:{ background:'var(--chip-bg)', color:'var(--text)' },
    ghost:{ background:'transparent', color:'var(--primary)', padding:0, height:'auto' },
  }[variant];
  const lift = (variant==='primary'||variant==='cyan') && hover ? { transform:'translateY(-2px)' } : (hover && variant!=='ghost' ? { opacity:.9 } : {});
  const props = {
    onMouseEnter:()=>setHover(true), onMouseLeave:()=>setHover(false), onClick,
    style:{ ...base, ...sizes, ...variants, ...lift, ...style },
  };
  const content = <>{children}{icon && <Icon name={icon} size={size==='lg'?19:17} color="currentColor" />}</>;
  return href ? <a href={href} {...props}>{content}</a> : <button {...props}>{content}</button>;
}

/* ---------- Badge / eyebrow pill ---------- */
function Badge({ children, dot=true }) {
  return (
    <span style={{ display:'inline-flex', alignItems:'center', gap:8, fontFamily:'var(--font-sans)', fontWeight:600,
      fontSize:11, letterSpacing:'.12em', textTransform:'uppercase', color:'var(--text-2)',
      background:'var(--chip-bg)', border:'1px solid var(--border)', padding:'7px 15px', borderRadius:'var(--radius-pill)',
      whiteSpace:'nowrap', maxWidth:'100%', overflow:'hidden' }}>
      {dot && <span style={{ width:6, height:6, borderRadius:9999, flexShrink:0, background:'var(--cyan)', boxShadow:'0 0 8px var(--cyan)' }}></span>}
      {children}
    </span>
  );
}

function Container({ children, style={} }) {
  return <div className="container" style={style}>{children}</div>;
}

/* ---------- Reveal (IntersectionObserver) ---------- */
function useReveal() {
  React.useEffect(() => {
    // Instantly reveal anything in the hero (above the fold) on load
    const heroEls = document.querySelectorAll('section:first-of-type .reveal, .hero-instant .reveal');
    heroEls.forEach(e => e.classList.add('is-in'));

    // Scroll-reveal for everything else
    const els = document.querySelectorAll('.reveal:not(.is-in)');
    if (!('IntersectionObserver' in window)) { els.forEach(e=>e.classList.add('is-in')); return; }
    const io = new IntersectionObserver((entries) => {
      entries.forEach(en => { if (en.isIntersecting) { en.target.classList.add('is-in'); io.unobserve(en.target); } });
    }, { threshold:0.12, rootMargin:'0px 0px -8% 0px' });
    els.forEach(e => io.observe(e));
    return () => io.disconnect();
  }, []);
}
function Reveal({ children, delay=0, style={}, as='div', className='' }) {
  return React.createElement(as, { className:`reveal ${className}`, style:{ transitionDelay:`${delay}ms`, ...style } }, children);
}

/* ---------- Animated stat counter ---------- */
function Stat({ value, prefix='', suffix='', label, sub, color }) {
  const ref = React.useRef(null);
  const [disp, setDisp] = React.useState(0);
  React.useEffect(() => {
    const el = ref.current; if (!el) return;
    let done = false;
    const run = () => {
      if (done) return; done = true;
      const dur = 1500, t0 = performance.now();
      const tick = (now) => {
        const p = Math.min(1, (now - t0) / dur);
        const e = 1 - Math.pow(1 - p, 3);
        setDisp(value * e);
        if (p < 1) requestAnimationFrame(tick);
      };
      requestAnimationFrame(tick);
    };
    const io = new IntersectionObserver((ents) => ents.forEach(en => { if (en.isIntersecting) run(); }), { threshold:0.4 });
    io.observe(el);
    return () => io.disconnect();
  }, [value]);
  const isFloat = value % 1 !== 0;
  const shown = isFloat ? disp.toFixed(1) : Math.round(disp).toLocaleString();
  return (
    <div ref={ref}>
      <div style={{ fontFamily:'var(--font-mono)', fontWeight:500, fontSize:'clamp(40px,5vw,60px)', lineHeight:1, letterSpacing:'-2px', color: color || 'var(--text)' }}>
        {prefix}{shown}{suffix}
      </div>
      {label && <div style={{ marginTop:12, fontFamily:'var(--font-sans)', fontWeight:600, fontSize:15, color:'var(--text)' }}>{label}</div>}
      {sub && <div style={{ marginTop:4, fontFamily:'var(--font-sans)', fontSize:13, color:'var(--text-3)' }}>{sub}</div>}
    </div>
  );
}

/* ---------- Theme toggle ---------- */
function ThemeToggle({ compact=false }) {
  const [theme, setTheme] = React.useState(() => document.documentElement.getAttribute('data-theme') || 'dark');
  const flip = () => {
    const next = theme === 'dark' ? 'light' : 'dark';
    document.documentElement.setAttribute('data-theme', next);
    try { localStorage.setItem('do-theme', next); } catch(e){}
    setTheme(next);
  };
  return (
    <button onClick={flip} aria-label="Toggle theme" title="Toggle theme"
      style={{ width:40, height:40, borderRadius:9999, border:'1px solid var(--border)', background:'var(--chip-bg)',
        cursor:'pointer', display:'flex', alignItems:'center', justifyContent:'center', color:'var(--text)', flexShrink:0,
        transition:'background .15s ease' }}>
      <Icon name={theme === 'dark' ? 'sun' : 'moon'} size={18} color="currentColor" />
    </button>
  );
}

/* ---------- Section heading block ---------- */
function SectionHead({ eyebrow, title, body, align='left', maxBody=560, style={} }) {
  const center = align === 'center';
  return (
    <div className="reveal" style={{ textAlign: center ? 'center':'left', marginBottom:56, ...style }}>
      {eyebrow && <div className="eyebrow" style={{ marginBottom:18 }}>{eyebrow}</div>}
      <h2 className="do-display-lg" style={{ margin:0, maxWidth: center ? 760:760, marginLeft: center?'auto':0, marginRight: center?'auto':0 }}>{title}</h2>
      {body && <p className="do-body-lg" style={{ marginTop:20, maxWidth:maxBody, marginLeft: center?'auto':0, marginRight: center?'auto':0 }}>{body}</p>}
    </div>
  );
}

/* ---------- Launch mark: logo rocket flies out of the circle, looping ---------- */
function LaunchMark({ size = 64 }) {
  return (
    <div style={{ position:'relative', width:size, height:size, display:'inline-flex', alignItems:'center', justifyContent:'center' }}>
      {/* circle badge */}
      <div style={{ position:'absolute', inset:0, borderRadius:'9999px', background:'var(--navy-deep)', border:'2px solid rgba(26,224,230,.55)', boxShadow:'0 0 24px rgba(26,224,230,.25)' }}></div>
      {/* faint orbiting ring */}
      <div style={{ position:'absolute', inset:6, borderRadius:'9999px', border:'1.5px dashed rgba(26,224,230,.3)', animation:'do-spin 16s linear infinite' }}></div>
      {/* exhaust trail */}
      <span className="lm-trail" style={{ position:'absolute', left:'50%', top:'50%', width:size*0.5, height:2, marginLeft:-size*0.25, borderRadius:2,
        background:'linear-gradient(90deg, transparent, var(--cyan))', transformOrigin:'right center', animation:'lm-trail 3.4s ease-in-out infinite' }}></span>
      {/* rocket */}
      <span className="lm-rocket" style={{ position:'relative', display:'inline-flex', animation:'lm-launch 3.4s ease-in-out infinite' }}>
        <Icon name="rocket" size={size*0.44} color="var(--cyan)" />
      </span>
    </div>
  );
}

Object.assign(window, { Icon, Orbit, Logo, Button, Badge, Container, useReveal, Reveal, Stat, ThemeToggle, SectionHead, LaunchMark });
