// Shared primitives for the dark draft. Loaded first so every other part can
// reach for the same heading treatment, reveal behaviour, and node/dot marks
// instead of re-rolling them six times.

// Section headings: Instrument Serif, tight, with a sand-tinted gradient fill
// so the top of the letterform catches the ambient light.
const nlHeadingStyle = {
  fontFamily: "'Instrument Serif', serif",
  fontWeight: 400,
  fontSize: 'clamp(34px, 4.6vw, 58px)',
  lineHeight: 1.04,
  letterSpacing: '-0.025em',
  margin: 0,
  maxWidth: 860,
  // background-clip:text paints the gradient only inside the element's
  // background box, and a 1.04 line-height makes that box shorter than the
  // descenders reach — so the tails of g, y, p get sliced off. Pad the box out
  // and pull the same amount back off the margin, which buys the descenders
  // room to paint without costing any layout height.
  paddingBottom: '0.16em',
  marginBottom: '-0.16em',
  background: 'linear-gradient(170deg, #FFFFFF 0%, #DCE0E8 48%, #9FA7B5 100%)',
  WebkitBackgroundClip: 'text',
  backgroundClip: 'text',
  color: 'transparent',
  WebkitTextFillColor: 'transparent',
};

// Italic accent word inside a heading. The gradient fill above clips to the
// text, so an accent span has to opt back out of it to take its own colour.
const nlAccentStyle = {
  fontStyle: 'italic',
  background: 'none',
  WebkitTextFillColor: 'var(--sand)',
  color: 'var(--sand)',
};

// Scroll reveal. Fires once, then disconnects — nothing re-animates on the way
// back up the page. Degrades to "always visible" where there's no observer.
function NlReveal({ children, delay = 0, as = 'div', style, className, ...rest }) {
  const ref = React.useRef(null);
  const [shown, setShown] = React.useState(false);

  React.useEffect(() => {
    const el = ref.current;
    if (!el || typeof IntersectionObserver === 'undefined') { setShown(true); return; }
    const io = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) { setShown(true); io.disconnect(); }
    }, { threshold: 0.12, rootMargin: '0px 0px -8% 0px' });
    io.observe(el);
    return () => io.disconnect();
  }, []);

  return React.createElement(as, {
    ...rest,
    ref,
    className,
    style: {
      ...style,
      opacity: shown ? 1 : 0,
      transform: shown ? 'none' : 'translateY(20px)',
      transition: `opacity 760ms var(--ease) ${delay}ms, transform 760ms var(--ease) ${delay}ms`,
    },
  }, children);
}

// Emerald micro-indicator. `on` lights it; otherwise it sits back as a grey tick.
function NlDot({ on = true, size = 5 }) {
  return (
    <span
      className={on ? 'dot' : 'dot dot-idle'}
      style={{ width: size, height: size }}
    />
  );
}

window.nlHeadingStyle = nlHeadingStyle;
window.nlAccentStyle = nlAccentStyle;
window.NlReveal = NlReveal;
window.NlDot = NlDot;
