/* global React */
/* ============================================================
   Primitives — typographic + atmospheric building blocks.
   ============================================================ */
const { useEffect, useRef, useState, useContext, createContext, useMemo, useCallback } = React;

/* ----- Tweaks context — pages read live values from here ----- */
const TweaksContext = createContext({});
const useTw = () => useContext(TweaksContext);

/* ----- Router context (set by router.jsx) ----- */
const RouterContext = createContext({ path: '/', go: () => {} });
const useRouter = () => useContext(RouterContext);

/* ============================================================
   Eyebrow — the structural ALL CAPS label
   ============================================================ */
function Eyebrow({ children, style, color }) {
  return (
    <div style={{
      fontSize: 11,
      letterSpacing: '0.26em',
      textTransform: 'uppercase',
      color: color || 'var(--accent)',
      fontWeight: 500,
      ...style,
    }}>{children}</div>
  );
}

/* ============================================================
   Display number — used for stats, indices
   ============================================================ */
function Idx({ children, style }) {
  return (
    <span style={{
      fontSize: 11,
      color: 'var(--c-slate-ocean)',
      letterSpacing: '0.22em',
      fontWeight: 500,
      fontVariantNumeric: 'tabular-nums',
      ...style,
    }}>{children}</span>
  );
}

/* ============================================================
   Button — primary gold, ghost outline, text-only.
   No bounce. Hover slides the arrow 3px.
   ============================================================ */
function Button({ variant = 'primary', children, icon = 'arrow-right', onClick, style, href, type = 'button', size = 'md', noIcon = false }) {
  const router = useRouter();
  const [hover, setHover] = useState(false);

  const handleClick = (e) => {
    if (href && href.startsWith('/')) {
      e.preventDefault();
      router.go(href);
    }
    onClick && onClick(e);
  };

  const sizes = {
    sm: { padding: '10px 18px', fontSize: 12 },
    md: { padding: '14px 26px', fontSize: 13 },
    lg: { padding: '18px 32px', fontSize: 14 },
  };

  const base = {
    fontFamily: 'var(--font-sans)',
    fontWeight: 500,
    cursor: 'pointer',
    borderRadius: 2,
    letterSpacing: '0.04em',
    textTransform: 'uppercase',
    transition: 'background 220ms var(--ease-out), border-color 220ms var(--ease-out), color 220ms var(--ease-out), box-shadow 220ms var(--ease-out)',
    display: 'inline-flex',
    alignItems: 'center',
    gap: 10,
    border: '1px solid transparent',
    textDecoration: 'none',
    ...sizes[size],
  };

  const variants = {
    primary: {
      background: 'var(--c-glow-500)',
      color: 'var(--c-imperial-indigo)',
      borderColor: 'var(--c-glow-500)',
    },
    ghost: {
      background: 'transparent',
      color: 'var(--c-steel-100)',
      borderColor: 'var(--line-strong)',
    },
    text: {
      background: 'transparent',
      color: 'var(--c-steel-100)',
      padding: '6px 0',
      borderRadius: 0,
      border: 0,
      borderBottom: '1px solid transparent',
      letterSpacing: '0.18em',
      fontSize: 11,
    },
    dark: {
      background: 'var(--c-nightfall)',
      color: 'var(--c-steel-100)',
      borderColor: 'var(--line-strong)',
    },
  };

  const hoverStyle = hover ? {
    primary: {
      background: 'var(--c-glow-400)',
      borderColor: 'var(--c-glow-400)',
      boxShadow: '0 0 0 1px rgba(212,169,66,0.3), 0 18px 48px rgba(212,169,66,0.18)',
    },
    ghost: {
      background: 'rgba(255,255,255,0.04)',
      borderColor: 'rgba(230,233,238,0.55)',
      color: '#fff',
    },
    text: {
      borderBottomColor: 'var(--c-glow-500)',
      color: '#fff',
    },
    dark: {
      background: 'var(--c-admiral-ink)',
      borderColor: 'rgba(230,233,238,0.4)',
      color: '#fff',
    },
  }[variant] : {};

  const Tag = href ? 'a' : 'button';
  const props = href
    ? { href, onClick: handleClick }
    : { type, onClick: handleClick };

  return (
    <Tag {...props}
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
      style={{ ...base, ...variants[variant], ...hoverStyle, ...style }}>
      <span>{children}</span>
      {icon && !noIcon && (
        <i data-lucide={icon} style={{
          width: 14, height: 14,
          transition: 'transform 240ms var(--ease-out)',
          transform: hover ? 'translateX(3px)' : 'none',
        }}></i>
      )}
    </Tag>
  );
}

/* ============================================================
   Beam — the signature directional gradient.
   Reads beam intensity from Tweaks context.
   ============================================================ */
function Beam({
  angle = -9,
  intensity,
  from = '4%',
  width = '75%',
  height = '170%',
  top = '-15%',
  drift = true,
  soft = false,
  zIndex = 0,
  sweep = false,
}) {
  const tw = useTw();
  const beamMult = tw.beamIntensity ?? 0.55;
  const alpha = (intensity ?? 0.55) * (beamMult / 0.55);
  const motionMult = tw.motion === 'still' ? 0 : tw.motion === 'lively' ? 1.6 : 1;
  const ref = useRef(null);
  const [lit, setLit] = useState(!sweep);

  // Lighthouse sweep-in: beam swings from off-angle/dark into resting
  // position the first time it enters the viewport (or on mount, if
  // already in view — e.g. the hero).
  useEffect(() => {
    if (!sweep) return;
    if (tw.motion === 'still') { setLit(true); return; }
    const el = ref.current; if (!el) return;
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          requestAnimationFrame(() => setLit(true));
          io.disconnect();
        }
      });
    }, { threshold: 0.2 });
    io.observe(el);
    return () => io.disconnect();
  }, [sweep, tw.motion]);

  return (
    <div ref={ref} aria-hidden style={{
      position: 'absolute',
      left: from, top,
      width, height,
      background: `linear-gradient(112deg, rgba(212,169,66,${alpha}) 0%, rgba(243,222,170,${alpha * 0.6}) 22%, rgba(212,169,66,${alpha * 0.25}) 50%, rgba(212,169,66,0) 80%)`,
      transform: sweep && !lit ? `rotate(${angle - 26}deg) translateX(-18%)` : `rotate(${angle}deg) translateX(0)`,
      transformOrigin: 'top left',
      pointerEvents: 'none',
      zIndex,
      opacity: sweep ? (lit ? 1 : 0) : 1,
      transition: sweep ? 'opacity 1400ms var(--ease-out), transform 1900ms var(--ease-out)' : 'none',
      animation: (drift && motionMult > 0 && (!sweep || lit))
        ? `${soft ? 'beam-drift-soft' : 'beam-drift'} ${11 / motionMult}s ease-in-out infinite ${sweep ? '1900ms' : '0s'}`
        : 'none',
      mixBlendMode: 'screen',
      filter: 'blur(2px)',
    }} />
  );
}

/* ============================================================
   HoverSweep — a beam of light catching a card on hover.
   Drop inside a `position: relative` container with `active`
   bound to the container's hover state.
   ============================================================ */
function HoverSweep({ active }) {
  return (
    <div aria-hidden style={{
      position: 'absolute',
      inset: 0,
      background: 'linear-gradient(115deg, transparent 35%, rgba(212,169,66,0.12) 50%, transparent 65%)',
      transform: active ? 'translateX(0%)' : 'translateX(-130%)',
      transition: 'transform 750ms var(--ease-out)',
      pointerEvents: 'none',
      mixBlendMode: 'screen',
    }} />
  );
}

/* ============================================================
   Reveal — IntersectionObserver fade-up.
   Honors motion tweak (still = instant on).
   ============================================================ */
function Reveal({ children, delay = 0, style, as: As = 'div' }) {
  const ref = useRef(null);
  const tw = useTw();
  const motion = tw.motion;
  useEffect(() => {
    const el = ref.current; if (!el) return;
    if (motion === 'still') { el.classList.add('on'); return; }
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          setTimeout(() => el.classList.add('on'), delay);
          io.disconnect();
        }
      });
    }, { threshold: 0.1, rootMargin: '0px 0px -80px 0px' });
    io.observe(el);
    return () => io.disconnect();
  }, [delay, motion]);
  return <As ref={ref} className="reveal" style={style}>{children}</As>;
}

/* ============================================================
   PortraitSlot — labelled placeholder for editorial portraits.
   Each labelled with the brand book's locked direction.
   Reads --tdp-img-opacity for tweak-controlled luminance.
   ============================================================ */
function PortraitSlot({
  label = 'tan blazer · seated · serious',
  ratio = '4 / 5',
  tone = 'warm',
  src,
  style,
  showLabel = true,
}) {
  const tones = {
    warm: {
      bg: 'linear-gradient(165deg, #2a3f5a 0%, #1a2b44 38%, #0a1939 78%, #06112c 100%)',
      silhouette: 'radial-gradient(ellipse 26% 16% at 50% 22%, rgba(195,168,128,0.42), transparent 65%), radial-gradient(ellipse 44% 38% at 50% 64%, rgba(120,88,52,0.55), transparent 70%)',
      rim: 'linear-gradient(265deg, rgba(212,169,66,0.22), transparent 38%)',
    },
    cool: {
      bg: 'linear-gradient(165deg, #1c3146 0%, #0e2236 38%, #081827 78%, #04101e 100%)',
      silhouette: 'radial-gradient(ellipse 26% 16% at 50% 22%, rgba(180,194,214,0.38), transparent 65%), radial-gradient(ellipse 44% 38% at 50% 64%, rgba(70,98,128,0.55), transparent 70%)',
      rim: 'linear-gradient(265deg, rgba(180,200,228,0.18), transparent 40%)',
    },
    paper: {
      bg: 'linear-gradient(160deg, #d9d4c6 0%, #c2bba9 100%)',
      silhouette: 'radial-gradient(ellipse 26% 16% at 50% 22%, rgba(120,98,68,0.6), transparent 65%), radial-gradient(ellipse 42% 36% at 50% 64%, rgba(85,72,52,0.65), transparent 70%)',
      rim: 'linear-gradient(265deg, rgba(180,156,120,0.25), transparent 38%)',
    },
  }[tone];

  return (
    <div style={{
      position: 'relative',
      width: '100%',
      aspectRatio: ratio,
      background: src ? `center/cover no-repeat url(${src})` : tones.bg,
      borderRadius: 4,
      overflow: 'hidden',
      boxShadow: tone === 'paper'
        ? 'var(--shadow-md)'
        : '0 0 0 1px rgba(212,169,66,0.16), 0 30px 90px rgba(212,169,66,0.10), 0 40px 120px rgba(0,0,0,0.45)',
      opacity: 'var(--tdp-img-opacity)',
      ...style,
    }}>
      {!src && (
        <>
          <div style={{ position: 'absolute', inset: 0, background: tones.silhouette, mixBlendMode: 'screen', opacity: 0.9 }} />
          <div style={{ position: 'absolute', inset: 0, background: tones.rim, mixBlendMode: 'screen' }} />
          <div style={{ position: 'absolute', inset: 0, background: 'radial-gradient(circle at 70% 80%, transparent 40%, rgba(0,0,0,0.4) 100%)', pointerEvents: 'none' }} />
          {showLabel && (
            <div style={{
              position: 'absolute', left: 16, bottom: 14, right: 16,
              display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end',
              fontSize: 9, letterSpacing: '0.24em', textTransform: 'uppercase',
              color: tone === 'paper' ? 'rgba(60,52,38,0.65)' : 'rgba(243,222,170,0.5)',
              fontWeight: 500,
            }}>
              <span>Portrait</span>
              <span style={{ textAlign: 'right', maxWidth: '60%' }}>{label}</span>
            </div>
          )}
        </>
      )}
    </div>
  );
}

/* ============================================================
   ImageSlot — generic atmospheric image placeholder
   (hands, posture, boardroom, environment).
   ============================================================ */
function ImageSlot({ label = 'hands on desk · low light', ratio = '4 / 3', tone = 'cool', style, showLabel = true, src }) {
  const tones = {
    cool: {
      bg: 'radial-gradient(80% 70% at 30% 40%, #1a2b44 0%, #0e2236 50%, #081227 100%)',
      detail: 'radial-gradient(ellipse 24% 32% at 36% 56%, rgba(150,168,196,0.18), transparent 70%), radial-gradient(ellipse 30% 20% at 70% 70%, rgba(60,86,118,0.5), transparent 70%)',
    },
    warm: {
      bg: 'radial-gradient(80% 70% at 30% 40%, #2a3a4e 0%, #14202f 50%, #0a121f 100%)',
      detail: 'radial-gradient(ellipse 24% 32% at 36% 56%, rgba(195,168,128,0.22), transparent 70%), radial-gradient(ellipse 30% 20% at 70% 70%, rgba(120,88,52,0.5), transparent 70%)',
    },
    paper: {
      bg: 'radial-gradient(80% 70% at 30% 40%, #ece7da 0%, #d4cebd 80%)',
      detail: 'radial-gradient(ellipse 30% 22% at 64% 60%, rgba(85,72,52,0.18), transparent 70%)',
    },
  }[tone];
  return (
    <div style={{
      position: 'relative',
      width: '100%',
      aspectRatio: ratio,
      background: src ? `center/cover no-repeat url(${src})` : tones.bg,
      overflow: 'hidden',
      borderRadius: 2,
      opacity: 'var(--tdp-img-opacity)',
      ...style,
    }}>
      {!src && (
        <div style={{ position: 'absolute', inset: 0, background: tones.detail, mixBlendMode: tone === 'paper' ? 'multiply' : 'screen' }} />
      )}
      {showLabel && !src && (
        <div style={{
          position: 'absolute', inset: 'auto 16px 14px 16px',
          fontSize: 9, letterSpacing: '0.24em', textTransform: 'uppercase',
          color: tone === 'paper' ? 'rgba(60,52,38,0.55)' : 'rgba(243,222,170,0.4)',
          fontWeight: 500,
        }}>{label}</div>
      )}
    </div>
  );
}

/* ============================================================
   ThreadDivider — gold-thread section break, optional label
   ============================================================ */
function ThreadDivider({ label, align = 'left', style }) {
  return (
    <div style={{
      display: 'flex',
      alignItems: 'center',
      gap: 18,
      justifyContent: align === 'center' ? 'center' : 'flex-start',
      ...style,
    }}>
      <div className="thread-gold" />
      {label && <Eyebrow>{label}</Eyebrow>}
    </div>
  );
}

/* ============================================================
   ScrollHint — quiet "scroll" affordance
   ============================================================ */
function ScrollHint({ label = 'scroll', style }) {
  return (
    <div style={{
      display: 'inline-flex', alignItems: 'center', gap: 10,
      color: 'var(--c-slate-ocean)',
      fontSize: 10, letterSpacing: '0.32em',
      textTransform: 'uppercase', fontWeight: 500,
      ...style,
    }}>
      <span style={{
        display: 'inline-block',
        width: 24, height: 1,
        background: 'currentColor',
        opacity: 0.6,
      }} />
      {label}
    </div>
  );
}

/* ============================================================
   InlineLink — quiet text link with gold underline reveal
   ============================================================ */
function InlineLink({ href, children, onClick }) {
  const router = useRouter();
  const [hover, setHover] = useState(false);
  const click = (e) => {
    if (href && href.startsWith('/')) { e.preventDefault(); router.go(href); }
    onClick && onClick(e);
  };
  return (
    <a href={href || '#'} onClick={click}
       onMouseEnter={() => setHover(true)}
       onMouseLeave={() => setHover(false)}
       style={{
         color: hover ? 'var(--c-glow-200)' : 'var(--c-glow-500)',
         borderBottom: `1px solid ${hover ? 'var(--c-glow-500)' : 'transparent'}`,
         transition: 'all 220ms var(--ease-out)',
         textDecoration: 'none',
       }}>{children}</a>
  );
}

/* ============================================================
   PullQuote — italic, narrow, gold-thread left border
   ============================================================ */
function PullQuote({ children, attribution, style }) {
  return (
    <figure style={{ margin: 0, ...style }}>
      <blockquote style={{
        margin: 0,
        paddingLeft: 28,
        borderLeft: '1px solid var(--accent)',
        fontSize: 'clamp(22px, 2.2vw, 32px)',
        fontWeight: 300,
        fontStyle: 'italic',
        lineHeight: 1.3,
        letterSpacing: '-0.01em',
        color: 'var(--fg-strong)',
        maxWidth: '28ch',
      }}>{children}</blockquote>
      {attribution && (
        <figcaption style={{
          marginTop: 16, paddingLeft: 28,
          fontSize: 11, letterSpacing: '0.2em', textTransform: 'uppercase',
          color: 'var(--c-slate-ocean)',
        }}>{attribution}</figcaption>
      )}
    </figure>
  );
}

Object.assign(window, {
  TweaksContext, useTw,
  RouterContext, useRouter,
  Eyebrow, Idx, Button, Beam, Reveal, HoverSweep,
  PortraitSlot, ImageSlot, ThreadDivider, ScrollHint,
  InlineLink, PullQuote,
});
