/* global React */
/* 7151 Johnston Road — shared building blocks.
   Reads DS primitives off the compiled bundle namespace; only composition
   and the swappable media system live here. Exported to window for the
   section files. */
const EE = window.EstablishedEstateDesignSystem_03388c;
const { Eyebrow, Rule, Button, StatBlock, EstateIcon } = EE;

/* ---------- type helpers (mirror the DS display/body voice) ---------- */
const dsp = (size, extra = {}) => ({
  fontFamily: 'var(--font-display)', fontWeight: 500, lineHeight: 1.08,
  letterSpacing: '-0.012em', color: 'var(--text-primary)', margin: 0,
  fontSize: size, textWrap: 'pretty', ...extra,
});
/* Responsive display sizes — the DS fixed 56px/42px choke in split columns,
   so headlines scale fluidly between mobile and the 1480px editorial width. */
const EE_DISPLAY = 'clamp(2rem, 1.05rem + 2.4vw, 3.4rem)';   /* section openers */
const EE_DISPLAY_3 = 'clamp(1.7rem, 1.05rem + 1.7vw, 2.6rem)'; /* story / thesis */
const bdy = (extra = {}) => ({
  fontFamily: 'var(--font-body)', fontSize: 'var(--type-body)',
  lineHeight: 'var(--leading-body)', color: 'var(--text-muted)',
  margin: 0, textWrap: 'pretty', ...extra,
});

/* ============================================================ MONOGRAM === */
function Monogram({ size = 44, color = 'var(--color-leather)' }) {
  return (
    <span style={{ display: 'inline-block', width: size * 0.8, height: size, color, lineHeight: 0 }}>
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 150" fill="none" style={{ width: '100%', height: '100%' }}>
        <g stroke="currentColor" fill="none" strokeLinecap="round" strokeLinejoin="round">
          <rect x="16" y="9" width="88" height="116" rx="44" strokeWidth="1.3" />
          <g strokeWidth="1.6">
            <g transform="translate(38,44)"><path d="M0 0 V44 M0 0 H17 M0 22 H13 M0 44 H17" /></g>
            <g transform="translate(51,50)"><path d="M0 0 V44 M0 0 H17 M0 22 H13 M0 44 H17" /></g>
          </g>
          <g strokeWidth="1">
            <path d="M82 112 q12 0 17 -10" />
            <path d="M87 111 q0 -5.5 5.5 -7.5 q0.5 5.5 -5.5 7.5 Z" />
            <path d="M92.5 107.5 q2 -5 7.5 -5.5 q-1.5 5 -7.5 5.5 Z" />
            <path d="M97.5 102.5 q3 -4.5 8.5 -4 q-2 4.6 -8.5 4 Z" />
          </g>
        </g>
      </svg>
    </span>
  );
}

/* ===================================================== REVEAL ON SCROLL === */
/* Wraps children in a div that fades + rises into view once. Calm, single-shot. */
function Reveal({ children, as = 'div', delay = 0, className = '', style, ...rest }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
      el.classList.add('is-in');
      return;
    }
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) { el.classList.add('is-in'); io.unobserve(el); }
      });
    }, { threshold: 0.12, rootMargin: '0px 0px -8% 0px' });
    io.observe(el);
    return () => io.disconnect();
  }, []);
  const Tag = as;
  return (
    <Tag ref={ref} className={`ee-reveal ${className}`} style={{ transitionDelay: `${delay}ms`, ...style }} {...rest}>
      {children}
    </Tag>
  );
}

/* ======================================================= PHOTO TONES ====== */
const PHOTO_TONES = {
  dawn: ['#e8d9c2', '#c7ad84'], pasture: ['#909c74', '#5e6a47'],
  ridge: ['#9aa6a0', '#6e7c75'], oak: ['#bcab8b', '#897556'],
  leather: ['#8a5d3a', '#5b3a21'], eucalyptus: ['#3a4738', '#222b21'],
  oat: ['#ddd1bf', '#c2b49a'], gold: ['#d8b878', '#a8823f'],
};

/* ============================================================ PHOTOSLOT === */
/* A REAL image slot for finalized photography. Pass `src` to drop the real
   curated photo in with zero layout change; until then it renders a refined
   golden-hour tonal placeholder, labelled as a photo slot. `ratio` keeps the
   frame fixed so swapping changes nothing around it. */
function PhotoSlot({
  id, src, alt, caption, tone = 'oak', icon, ratio = '4 / 3',
  scrim = false, rounded = true, compact = false, children, className = '', style,
}) {
  const [a, b] = PHOTO_TONES[tone] || PHOTO_TONES.oak;
  return (
    <figure
      data-slot={id}
      className={`ee-frame ${className}`}
      style={{
        margin: 0, aspectRatio: ratio,
        borderRadius: rounded ? 'var(--radius-lg)' : 0,
        background: `linear-gradient(155deg, ${a} 0%, ${b} 100%)`,
        display: 'flex', alignItems: 'flex-end',
        ...style,
      }}
    >
      {src ? <img src={src} alt={alt || caption || ''} loading="lazy" /> : null}
      {!src && icon ? (
        <span style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'rgba(255,253,248,0.28)' }}>
          <EstateIcon name={icon} size={compact ? 48 : 64} strokeWidth={1} />
        </span>
      ) : null}
      {!src && !compact ? (
        <span className="ee-slot-chip ee-slot-chip--quiet">Photo slot</span>
      ) : null}
      {scrim ? <span style={{ position: 'absolute', inset: 0, background: 'linear-gradient(to top, rgba(33,26,20,0.58), rgba(33,26,20,0.10) 46%, transparent 76%)' }} /> : null}
      {children ? <div style={{ position: 'relative', width: '100%', zIndex: 2 }}>{children}</div> : null}
      {caption ? (
        <figcaption style={{ position: 'relative', zIndex: 2, margin: 'var(--space-4)', marginLeft: 'auto', padding: '6px 10px', background: 'rgba(33,26,20,0.42)', backdropFilter: 'blur(2px)', borderRadius: 'var(--radius-sm)', fontFamily: 'var(--font-body)', fontSize: '11px', fontWeight: 600, letterSpacing: '0.16em', textTransform: 'uppercase', color: 'var(--color-white)' }}>{caption}</figcaption>
      ) : null}
    </figure>
  );
}

/* ============================================================ MEDIASLOT === */
/* The reusable SWAPPABLE placeholder wrapper for every unfinished asset
   (film, floor plan, estate map, CubiCasa). One named slot, one aspect ratio,
   one config reference — dropping in the final asset later changes nothing
   around it. `label` is the visible PLACEHOLDER chip; `src` (image) swaps it. */
function MediaSlot({
  id, label, note, ratio = '16 / 9', icon, src, alt,
  onDeep = false, children, className = '', style,
}) {
  return (
    <div
      data-slot={id}
      className={`ee-swap ${onDeep ? 'ee-swap--ondeep' : ''} ${className}`}
      style={{ aspectRatio: ratio, ...style }}
    >
      {src ? (
        <img src={src} alt={alt || label} loading="lazy" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
      ) : (
        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 'var(--space-4)', padding: 'var(--space-6)', textAlign: 'center' }}>
          {icon ? (
            <span style={{ color: onDeep ? 'var(--color-hay)' : 'var(--color-leather)', opacity: 0.85 }}>
              <EstateIcon name={icon} size={40} strokeWidth={1.25} />
            </span>
          ) : null}
          {note ? <p className={`ee-swap__note ${onDeep ? 'ee-note--deep' : ''}`}>{note}</p> : null}
        </div>
      )}
      {label ? (
        <span className={`ee-slot-chip ${onDeep ? 'ee-slot-chip--ondeep' : ''}`}>{label}</span>
      ) : null}
      {children}
    </div>
  );
}

/* ===================================================== GALLERY LIGHTBOX ===
   Reusable full-screen gallery modal shared by the Lived-Moments, Interiors,
   and Floor-Plan galleries. `items` = [{ src, caption, alt }]; `index` is the
   open slide. Proper modal: role="dialog", aria-modal, labelled by the live
   caption; traps focus, locks scroll, restores focus to the trigger on close;
   ← → keys / arrows / swipe navigate; neighbouring images preload. */
const EE_WEB = '../Assets/web/';   /* web-optimized photo root (rel. to page) */
function GalleryLightbox({ items, index, onClose, onNav, restoreEl, frame = false }) {
  const dialogRef = React.useRef(null);
  const closeRef = React.useRef(null);
  const touchX = React.useRef(null);
  const item = items[index];

  React.useEffect(() => {
    const prevFocus = restoreEl || document.activeElement;
    document.body.style.overflow = 'hidden';
    const raf = requestAnimationFrame(() => closeRef.current && closeRef.current.focus());
    const onKey = (e) => {
      if (e.key === 'Escape') { e.preventDefault(); onClose(); return; }
      if (e.key === 'ArrowRight') { e.preventDefault(); onNav(1); return; }
      if (e.key === 'ArrowLeft') { e.preventDefault(); onNav(-1); return; }
      if (e.key === 'Tab') {
        const root = dialogRef.current; if (!root) return;
        const f = root.querySelectorAll('button, [href], [tabindex]:not([tabindex="-1"])');
        if (!f.length) return;
        const first = f[0], last = f[f.length - 1];
        if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); }
        else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }
      }
    };
    document.addEventListener('keydown', onKey, true);
    return () => {
      document.removeEventListener('keydown', onKey, true);
      document.body.style.overflow = '';
      cancelAnimationFrame(raf);
      if (prevFocus && prevFocus.focus) prevFocus.focus();
    };
  }, [onClose, onNav, restoreEl]);

  React.useEffect(() => {
    [1, -1].forEach((d) => {
      const n = (index + d + items.length) % items.length;
      const img = new Image(); img.src = items[n].src;
    });
  }, [index, items]);

  const multi = items.length > 1;
  const onTouchStart = (e) => { touchX.current = e.changedTouches[0].clientX; };
  const onTouchEnd = (e) => {
    if (touchX.current == null) return;
    const dx = e.changedTouches[0].clientX - touchX.current;
    if (Math.abs(dx) > 44) onNav(dx < 0 ? 1 : -1);
    touchX.current = null;
  };

  return (
    <div className="ee-overlay ee-overlay--deep" onClick={onClose}>
      <div
        ref={dialogRef}
        className="ee-lightbox"
        role="dialog" aria-modal="true" aria-labelledby="ee-glb-cap"
        onClick={(e) => e.stopPropagation()}
        onTouchStart={onTouchStart} onTouchEnd={onTouchEnd}
      >
        <button ref={closeRef} className="ee-overlay__close" onClick={onClose} aria-label="Close gallery">
          <svg width="18" height="18" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1.6" fill="none" strokeLinecap="round"><path d="M5 5l14 14M19 5L5 19" /></svg>
        </button>
        {multi ? (
          <React.Fragment>
            <button className="ee-lightbox__nav" style={{ left: 16 }} onClick={() => onNav(-1)} aria-label="Previous">
              <svg width="22" height="22" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1.6" fill="none" strokeLinecap="round" strokeLinejoin="round"><path d="M15 5l-7 7 7 7" /></svg>
            </button>
            <button className="ee-lightbox__nav" style={{ right: 16 }} onClick={() => onNav(1)} aria-label="Next">
              <svg width="22" height="22" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1.6" fill="none" strokeLinecap="round" strokeLinejoin="round"><path d="M9 5l7 7-7 7" /></svg>
            </button>
          </React.Fragment>
        ) : null}
        <figure className={`ee-lightbox__figure${frame ? ' ee-lightbox__figure--card' : ''}`}>
          <img className="ee-lightbox__img" src={item.src} alt={item.alt || item.caption || ''} style={{ objectFit: 'contain' }} />
          <figcaption id="ee-glb-cap" className="ee-lightbox__caption">
            {item.caption || ''}{multi ? <span className="ee-lightbox__count">{item.caption ? ' · ' : ''}{index + 1} / {items.length}</span> : null}
          </figcaption>
        </figure>
      </div>
    </div>
  );
}

/* ============================================================= SECTION ==== */
function Section({ children, tone = 'page', tight = false, wide = false, id, style }) {
  const cls = `ee-section ee-section--${tone}${tight ? ' ee-section--tight' : ''}`;
  return (
    <section id={id} className={cls} style={style}>
      <div className={`ee-container${wide ? ' ee-container--wide' : ''}`}>{children}</div>
    </section>
  );
}

/* A small reusable section header (rule tick + eyebrow + headline). `measure`
   caps the block width on wide screens (px/rem); omit it inside split columns
   so the headline fills its column instead of choking on a ch-based cap. */
function SectionHead({ eyebrow, title, body, tone = 'light', align = 'left', measure, ruleTone, style }) {
  const onDeep = tone === 'deep';
  const eColor = onDeep ? 'var(--color-hay)' : 'var(--text-accent)';
  const rTone = ruleTone || (onDeep ? 'hay' : 'leather');
  return (
    <div style={{ maxWidth: measure || 'none', ...(align === 'center' ? { marginInline: 'auto', textAlign: 'center' } : null), ...style }}>
      <Rule tone={rTone} length="48px" style={{ marginBottom: 'var(--space-5)', ...(align === 'center' ? { marginInline: 'auto' } : null) }} />
      {eyebrow ? <Eyebrow color={eColor} align={align} style={{ marginBottom: 'var(--space-4)' }}>{eyebrow}</Eyebrow> : null}
      {title ? <h2 style={dsp(EE_DISPLAY, { color: onDeep ? 'var(--color-white)' : 'var(--text-primary)' })}>{title}</h2> : null}
      {body ? <p style={bdy({ marginTop: 'var(--space-5)', color: onDeep ? 'var(--text-on-deep-muted)' : 'var(--text-muted)' })}>{body}</p> : null}
    </div>
  );
}

/* =============================================================== NAVBAR === */
const NAV_LINKS = [
  ['The Setting', '#setting'], ['The Residence', '#residence'],
  ['Equestrian', '#equestrian'], ['Estate Map', '#map'],
];
function Wordmark({ onDeep = false }) {
  return (
    <a href="#top" style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-3)', textDecoration: 'none' }}>
      <span style={{ lineHeight: 1.05 }}>
        <span style={{ display: 'block', fontFamily: 'var(--font-display)', fontSize: '21px', fontWeight: 500, color: onDeep ? 'var(--color-white)' : 'var(--text-primary)' }}>7151 Johnston Road</span>
        <span style={{ display: 'block', fontFamily: 'var(--font-body)', fontSize: '8.5px', fontWeight: 600, letterSpacing: '0.22em', textTransform: 'uppercase', color: onDeep ? 'rgba(255,253,248,0.6)' : 'var(--text-muted)', marginTop: '3px' }}>Tassajara Valley · Equestrian Estate</span>
      </span>
    </a>
  );
}
function NavBar({ onSchedule }) {
  return (
    <header className="ee-nav">
      <div className="ee-container ee-container--wide ee-nav__row">
        <Wordmark />
        <nav className="ee-nav__links">
          {NAV_LINKS.map(([l, href]) => (
            <a key={l} href={href} className="ee-nav__link">{l}</a>
          ))}
          <span className="ee-nav__cta--desktop">
            <Button variant="primary" size="sm" href="#action" onClick={onSchedule}>Schedule a Private Showing</Button>
          </span>
        </nav>
        <span className="ee-nav__cta--mobile">
          <Button variant="primary" size="sm" href="#action" onClick={onSchedule}>Schedule</Button>
        </span>
      </div>
    </header>
  );
}

/* ========================================================== STICKY CTA ==== */
function StickyCTA() {
  const [show, setShow] = React.useState(false);
  React.useEffect(() => {
    const onScroll = () => {
      const y = window.scrollY;
      const nearEnd = (window.innerHeight + y) > (document.body.scrollHeight - 900);
      setShow(y > 640 && !nearEnd);
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  return (
    <div className={`ee-sticky-cta${show ? ' is-visible' : ''}`}>
      <Button variant="primary" size="md" href="#action" style={{ width: '100%', whiteSpace: 'normal' }}>Schedule a Private Showing</Button>
    </div>
  );
}

Object.assign(window, {
  EEdsp: dsp, EEbdy: bdy, EE_DISPLAY, EE_DISPLAY_3, EE_WEB,
  Monogram, Reveal, PhotoSlot, MediaSlot, Section, SectionHead,
  NavBar, Wordmark, StickyCTA, PHOTO_TONES, GalleryLightbox,
});
