// ── Editorial placeholder image — a striped/gradient dark surface with
// mono labels, crop marks and a soft directional glow. Stands in for
// cinematic editorial photography without hand-drawn figurative SVG.

function Editorial({
  product,           // pass a product to seed
  label,             // optional override
  ratio = '3/4',     // aspect ratio
  glowX = 50,
  glowY = 40,
  variant = 'front', // shifts the glow + base hue for "different views"
  frame,
  className = '',
  children,
  style = {},
}) {
  // Seed off product or label
  const seedStr = (product?.id || label || 'lo') + variant;
  let s = 0;
  for (let i = 0; i < seedStr.length; i++) s = (s * 31 + seedStr.charCodeAt(i)) | 0;
  const rand = (n) => ((s = (s * 9301 + 49297) % 233280), (s / 233280) * n);

  const hueBase = product?.hue ?? rand(360);
  // Each "variant" reframes the same product
  const variantShift = { front: 0, back: 24, detail: -18, lifestyle: 8, flat: -6 }[variant] || 0;
  const hue = (hueBase + variantShift + 360) % 360;
  const lume = product?.lume ?? 14;
  const gx = glowX + (variant === 'back' ? 22 : variant === 'detail' ? -18 : 0);
  const gy = glowY + (variant === 'detail' ? 14 : 0);

  // Multi-layer dark base — looks slightly different per item but stays
  // in family. We avoid drawing figurative content; this just suggests
  // dramatic studio lighting on a dark subject.
  const base = `
    radial-gradient(80% 90% at ${gx}% ${gy}%,
      hsla(${hue}, 40%, ${lume + 14}%, .92),
      hsla(${hue}, 30%, ${lume + 3}%, .9) 38%,
      hsla(${hue}, 18%, ${Math.max(4, lume - 4)}%, 1) 75%),
    linear-gradient(180deg,
      hsla(${hue}, 14%, ${lume - 2}%, 1),
      hsla(${(hue + 8) % 360}, 16%, ${Math.max(2, lume - 8)}%, 1))
  `;

  const finalLabel = label || (product
    ? `${product.style} · ${product.finish}`
    : 'EDITORIAL · JT ATELIER');
  const frameLabel = frame || (product
    ? `FR · ${(Math.floor(rand(900)) + 100)}`
    : 'FR · 023');

  // Persistent drop target id — product+variant keeps a 4-up gallery distinct,
  // and unrelated frames get a slugged label so the journal etc still persist.
  const slug = (label || 'lo-frame').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
  const slotId = product ? `${product.id}-${variant}` : slug;
  const hint = product
    ? `Drop ${product.name.toLowerCase()} (${variant})`
    : `Drop image · ${(label || 'editorial').split('·')[0].trim().toLowerCase()}`;

  return (
    <div
      className={`ed ${className}`}
      style={{
        aspectRatio: ratio,
        background: base,
        '--glow-x': `${gx}%`,
        '--glow-y': `${gy}%`,
        ...style,
      }}
    >
      <div className="ed-glow" />
      <span className="ed-mark tl" />
      <span className="ed-mark tr" />
      <span className="ed-mark bl" />
      <span className="ed-mark br" />
      <span className="ed-label tl">{finalLabel}</span>
      <span className="ed-label br">{frameLabel}</span>
      {children}
    </div>
  );
}

// Particle field — subtle floating dots, used on hero.
function ParticleField({ count = 38, intensity = 1 }) {
  const dots = React.useMemo(() => {
    const arr = [];
    for (let i = 0; i < count; i++) {
      arr.push({
        left: Math.random() * 100,
        bottom: -Math.random() * 30,
        delay: -Math.random() * 18,
        dur: 12 + Math.random() * 18,
        dx: (Math.random() - 0.5) * 80,
        size: 1 + Math.random() * 2,
      });
    }
    return arr;
  }, [count]);
  return (
    <div className="particles" style={{ opacity: 0.5 + 0.5 * intensity }}>
      {dots.map((d, i) => (
        <span
          key={i}
          className="particle"
          style={{
            left: `${d.left}%`,
            bottom: `${d.bottom}%`,
            animationDelay: `${d.delay}s`,
            animationDuration: `${d.dur}s`,
            width: d.size,
            height: d.size,
            '--dx': `${d.dx}px`,
          }}
        />
      ))}
    </div>
  );
}

// Scroll reveal (.fade-up) + image blur-up + stagger.
// Idempotent, respects reduced-motion, and fail-safe (never leaves content hidden).
function useFadeUp() {
  React.useEffect(() => {
    const reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;

    // Image blur-up: fade photos in as they load (cached-safe).
    document.querySelectorAll('img:not([data-fade])').forEach((img) => {
      img.setAttribute('data-fade', '');
      const reveal = () => img.classList.add('is-loaded');
      if (reduce || (img.complete && img.naturalWidth > 0)) reveal();
      else {
        img.addEventListener('load', reveal, { once: true });
        img.addEventListener('error', reveal, { once: true });
      }
    });

    const els = document.querySelectorAll('.fade-up:not(.in)');
    if (!els.length) return;

    // Reduced motion or no IO support → reveal everything immediately.
    if (reduce || !('IntersectionObserver' in window)) {
      els.forEach((el) => el.classList.add('in'));
      return;
    }

    // Stagger: delay each element by its position among its fade-up siblings.
    els.forEach((el) => {
      if (el.style.getPropertyValue('--d')) return;
      const sibs = Array.prototype.filter.call(
        el.parentElement ? el.parentElement.children : [],
        (c) => c.classList && c.classList.contains('fade-up')
      );
      const i = Math.max(0, sibs.indexOf(el));
      el.style.setProperty('--d', Math.min(i * 0.07, 0.42) + 's');
    });

    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting) {
            e.target.classList.add('in');
            io.unobserve(e.target);
          }
        });
      },
      { threshold: 0.12, rootMargin: '0px 0px -8% 0px' }
    );
    els.forEach((el) => io.observe(el));

    // Fail-safe: guarantee everything is visible within 4s no matter what.
    const safety = setTimeout(() => {
      els.forEach((el) => el.classList.add('in'));
      document.querySelectorAll('img[data-fade=""]:not(.is-loaded)').forEach((img) => img.classList.add('is-loaded'));
    }, 4000);

    return () => { io.disconnect(); clearTimeout(safety); };
  });
}

function DropzoneHint() {
  const [hidden, setHidden] = React.useState(
    () => typeof window !== 'undefined' && localStorage.getItem('lo-dz-hint') === 'dismissed'
  );
  if (hidden) return null;
  const dismiss = () => {
    localStorage.setItem('lo-dz-hint', 'dismissed');
    setHidden(true);
  };
  return (
    <div className="dropzone-help" role="status">
      <svg width="22" height="22" viewBox="0 0 22 22" fill="none" stroke="currentColor" strokeWidth="1.3">
        <rect x="3" y="3" width="16" height="16" rx="1" />
        <path d="M3 14l4-4 4 4 3-3 5 5" />
        <circle cx="8" cy="8" r="1.3" />
      </svg>
      <div>
        <b>Drop your photos anywhere</b>
        Drag a jacket image onto any product frame — it persists across reloads.
      </div>
      <button onClick={dismiss} aria-label="Dismiss">
        <svg width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" strokeWidth="1.3">
          <line x1="1.5" y1="1.5" x2="8.5" y2="8.5" />
          <line x1="8.5" y1="1.5" x2="1.5" y2="8.5" />
        </svg>
      </button>
    </div>
  );
}

// Phase 2: hero parallax + nav scroll-state — one global passive rAF loop.
(function initChrome() {
  if (typeof window === 'undefined' || window._chromeInit) return;
  window._chromeInit = true;
  const reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  let ticking = false;
  function frame() {
    ticking = false;
    const y = window.scrollY || window.pageYOffset || 0;
    const vh = window.innerHeight || 800;
    const nav = document.querySelector('.nav');
    if (nav) nav.classList.toggle('is-scrolled', y > 36);

    // scroll-progress bar (created lazily)
    let bar = document.getElementById('scroll-progress');
    if (!bar) { bar = document.createElement('div'); bar.id = 'scroll-progress'; document.body.appendChild(bar); }
    const max = (document.documentElement.scrollHeight - vh) || 1;
    bar.style.width = Math.max(0, Math.min(100, (y / max) * 100)) + '%';

    if (!reduce) {
      // hero parallax
      const hero = document.querySelector('.hero-section');
      const bg = hero && hero.querySelector('[style*="background-image"]');
      if (bg) {
        const h = hero.offsetHeight || vh;
        bg.style.setProperty('--py', Math.min(y * 0.25, h * 0.05) + 'px');
      }
      // story-image parallax (drifts as it crosses the viewport)
      document.querySelectorAll('.parallax-img').forEach((el) => {
        const r = el.getBoundingClientRect();
        if (r.bottom < -40 || r.top > vh + 40) return;
        const fromCenter = (r.top + r.height / 2 - vh / 2) / vh;
        const py = Math.max(-1, Math.min(1, -fromCenter)) * (r.height * 0.06);
        el.style.setProperty('--py', py.toFixed(1) + 'px');
      });
    }
  }
  function onScroll() { if (!ticking) { ticking = true; requestAnimationFrame(frame); } }
  window.addEventListener('scroll', onScroll, { passive: true });
  window.addEventListener('resize', onScroll, { passive: true });
  frame();
})();

Object.assign(window, { Editorial, ParticleField, useFadeUp, DropzoneHint });
