const { useState, useEffect, useRef } = React;

// Reveal on scroll
function Reveal({ children, delay = 0, as: Tag = 'div', className = '', ...props }) {
  const ref = useRef(null);
  const [visible, setVisible] = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting) { setVisible(true); io.disconnect(); }
      });
    }, { threshold: 0.1, rootMargin: '0px 0px -60px 0px' });
    io.observe(el);
    return () => io.disconnect();
  }, []);
  return (
    <Tag
      ref={ref}
      className={`reveal ${visible ? 'in' : ''} ${className}`}
      style={{ transitionDelay: `${delay}s` }}
      {...props}
    >{children}</Tag>
  );
}

// Masked text reveal (letters come up from below a mask)
function RevealText({ children, delay = 0, className = '', as: Tag = 'span' }) {
  const ref = useRef(null);
  const [visible, setVisible] = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting) { setVisible(true); io.disconnect(); }
      });
    }, { threshold: 0.2 });
    io.observe(el);
    return () => io.disconnect();
  }, []);
  return (
    <Tag
      ref={ref}
      className={`reveal-mask ${visible ? 'in' : ''} ${className}`}
      style={{ '--delay': `${delay}s` }}
    ><span>{children}</span></Tag>
  );
}

// Infinite marquee — clones children to create seamless loop
function Marquee({ children, duration = 40, reverse = false, gap = 48 }) {
  const items = React.Children.toArray(children);
  return (
    <div
      className="marquee"
      data-reverse={reverse ? 'true' : 'false'}
      style={{ '--marquee-duration': `${duration}s`, '--marquee-gap': `${gap}px` }}
    >
      <div className="marquee-track">
        {items.map((c, i) => <React.Fragment key={i}>{c}</React.Fragment>)}
        {items.map((c, i) => <React.Fragment key={`b${i}`}>{c}</React.Fragment>)}
      </div>
    </div>
  );
}

// Live clock
function LiveClock({ tz = 'America/New_York', label = 'NYC' }) {
  const [time, setTime] = useState(() => fmt(tz));
  useEffect(() => {
    const id = setInterval(() => setTime(fmt(tz)), 1000);
    return () => clearInterval(id);
  }, [tz]);
  function fmt(zone) {
    return new Date().toLocaleTimeString('en-US', {
      hour: '2-digit', minute: '2-digit', second: '2-digit',
      hour12: false, timeZone: zone
    });
  }
  return <span>{label} {time}</span>;
}

// Content shared across directions
const SERVICES = [
  { num: '01', name: 'Brand Identity', desc: 'The name, the mark, and the rules that hold them together. Built to outlast a trend cycle, not chase one.', tags: ['Logo', 'Guidelines', 'Naming'] },
  { num: '02', name: 'Art Direction', desc: 'Casting, light, crop, reference. The difference between a brand people scroll past and one they stop for.', tags: ['Direction', 'Editorial', 'Photo'] },
  { num: '03', name: 'Digital Design', desc: 'Sites and interfaces that carry the identity instead of watering it down. Fast, odd where it counts, never generic.', tags: ['Web', 'Motion', 'Product'] },
  { num: '04', name: 'Strategy', desc: 'The uncomfortable questions first: what you stand for, what you refuse to be, and why anyone should care.', tags: ['Positioning', 'Voice', 'Research'] },
  { num: '05', name: 'Motion & Film', desc: 'Openers, loops, launch films. Movement that means something — not drift-and-fade wallpaper.', tags: ['Film', 'Titles', 'Animation'] },
];

const PROCESS = [
  { num: '01', name: 'Listen', desc: 'The first week is questions only. We want the thing under the thing — the real reason you picked up the phone.' },
  { num: '02', name: 'Cut', desc: 'Every option goes on the wall. Then almost all of them come down. What survives is the idea we would defend with rent money.' },
  { num: '03', name: 'Build', desc: 'Short cycles, shown rough and shown early. You see the work every week; nothing hides until a grand unveiling.' },
  { num: '04', name: 'Release', desc: 'We ship with guidelines a human can actually follow, then stay close while the brand meets the real world.' },
];

const JOURNAL = [
  { cat: 'Thinking', date: 'Apr 2026', title: 'A brief that spills onto page two isn’t finished', read: '6 min', slug: 'one-page-briefs' },
  { cat: 'Case Study', date: 'Mar 2026', title: 'The rebrand where we mostly removed things', read: '10 min', slug: 'rebrand-as-subtraction' },
  { cat: 'Essay', date: 'Feb 2026', title: 'No is a working tool, not a personality', read: '7 min', slug: 'saying-no' },
];

const MARQUEE_WORDS = [
  'Naming', 'Identity', 'Art Direction', 'Strategy', 'Motion', 'Web'
];

Object.assign(window, { Reveal, RevealText, Marquee, LiveClock, SERVICES, PROCESS, JOURNAL, MARQUEE_WORDS });
