/* Aevitan website — Home: hero, orbit signature, collection, philosophy, system. */
const DSh = window.AevitanDesignSystem_6a042d;
// Cart + ordering live in the authenticated portal (separate origin). Public CTAs route there to sign in.
const PORTAL_URL = (typeof location !== 'undefined' && location.hostname === 'localhost')
  ? 'http://localhost:3001'
  : 'https://portal.aevitan.com';
// B39 (a11y): in-content CTAs need a real root-relative href to be keyboard-focusable and to carry
// link semantics — the onClick still drives SPA nav (preventDefault). Uniquely named so it can't
// collide with another Babel script's top-level const (the kit shares one global lexical scope).
const RP_HOME = (window.AEV_ROUTES || {}).paths || {};

// [name, category, image, lowest catalog price (USD), description, productId|null]
// B49: this is only the OFFLINE fallback (id=null → the More+ opens the local drawer). When the
// live feed answers, the section shows the 4 real portfolio products most present in member
// wishlists (daily-rotating ties), each carrying its real id so More+ deep-links the shop modal.
const COLLECTION = [
  ['AEVI APEX-T', 'Metabolic peptide', '../../assets/aevi-apex-t.png', 420,
    'Sterile, lyophilized tirzepatide for metabolic and recovery protocols. Prescription-grade, third-party assayed, and serialized to the individual vial.', null],
  ['AEVI NOVEN', 'NAD+ supplement', '../../assets/aevi-noven.png', 95,
    'Sublingual NAD+ precursor strips for daily cellular-energy support. Batch-verified for identity and content, never marketing claims.', null],
  ['AEVI AMAZON', 'Daily capsules', '../../assets/aevi-amazon.png', 68,
    'NMN paired with Amazon-sourced actives in a daily capsule: the consistent foundation of an integrated longevity protocol.', null],
  ['AEVI DERMA', 'Skin & surface', '../../assets/aevi-derma.png', 140,
    'GHK-Cu topical cream for skin and surface vitality. Clinical-grade actives, dermatologist-formulated, traceable from batch to you.', null],
];

const PILLARS = [
  ['Research', 'Through proprietary algorithms, our platform continuously review thousands of scientific articles, clinical trials and clinical reports that may be used as a valuable tool to promote longevity and wellness.'],
  ['Curate', 'The most promising product candidates are submitted to our scientific board review to assess claiming, ADME (absorption, distribution, metabolism and elimination), directions and adverse reactions.'],
  ['Integrate', 'If the reward outweighs the risk when it comes to reported or measured effects and safety, the product is integrated in our portfolio.'],
];

const CLUB_PATHS = [
  ['How to belong', 'One free membership', 'Patients, health professionals, clinics and resellers all enter through the same basic signup. Additional information and a separate declaration are requested only when relevant to the selected member type or action.'],
  ['Member perk', 'Club standing', 'Club standing reflects sustained activity within the Club. It is a recognition of participation, not a separate way in, and it may qualify you for preferential pricing with participating consultants.'],
  ['The member benefit', 'Professional curation reports', 'Every member reads the board\u2019s opinion on each product. The professional report adds the full evaluation, the batch certificate of analysis and the manufacturer\u2019s package insert.'],
];

const SYSTEM = [
  ['Assess', 'Biomarkers & baseline', 'Longevity begins with measurement: biological age and biomarkers set the starting point.'],
  ['Metabolic', 'Peptides', 'Verified metabolic and recovery peptides, the engine of the protocol.'],
  ['Foundation', 'Supplements', 'Daily NAD+, NMN and actives: the consistent base that compounds over time.'],
  ['Surface', 'Cosmetics & topicals', 'Skin and surface care, so visible vitality matches internal health.'],
];

const ORBIT_ITEMS = [
  { label: 'Lab Tested', lx: 0, ly: -248,
    body: 'Every batch is sent to an independent laboratory. Quality control and consistency are a must.' },
  { label: 'Serialized', lx: 238, ly: -22,
    body: 'Each unit carries a unique serial and a tamper-evident seal, so no two products ever share an identity.' },
  { label: 'Scientific-reviewed', lx: 0, ly: 248,
    body: 'Our scientific board reviews every product and decides admission. Curation means saying NO far more often than YES.' },
  { label: 'Traceable', lx: -238, ly: 30,
    body: 'Scan a code to trace any unit from us to you.  We adopted the best practices to surface possible clones the moment they appear.' },
];
const ORBIT_DOTS = [30, 95, 150, 210, 270, 330];

/** Masked heading line — rises from an overflow mask when its section reveals. */
function Mask({ children, className = '' }) {
  return <span className={`aevmask ${className}`}><span className="aevmask__in">{children}</span></span>;
}

/* The orbit signature: scroll-scrubbed "explosion" from a lone core into the full
   ringed system, with strong 3D parallax, a core that expands A → Aevitan on hover,
   and orbiting trust labels that open a detail panel on hover. Fully JS-driven. */
function OrbitRing() {
  const { Kicker } = DSh;
  const secRef = React.useRef(null);
  const fieldRef = React.useRef(null);
  const coreRef = React.useRef(null);
  const coreARef = React.useRef(null);
  const coreFullRef = React.useRef(null);
  const panelRef = React.useRef(null);
  const [activeIdx, setActiveIdx] = React.useState(-1);
  const [isMobile, setIsMobile] = React.useState(false);
  const S = React.useRef({ mx: 0, my: 0, coreHover: false, activeIdx: -1 });

  React.useEffect(() => {
    const mq = window.matchMedia('(max-width:760px)');
    const on = () => setIsMobile(mq.matches);
    on();
    mq.addEventListener('change', on);
    return () => mq.removeEventListener('change', on);
  }, []);

  const setActive = (i) => { S.current.activeIdx = i; setActiveIdx(i); };
  const setCore = (v) => { S.current.coreHover = v; };

  React.useEffect(() => {
    if (isMobile) return undefined;
    const sec = secRef.current, field = fieldRef.current, core = coreRef.current;
    const aEl = coreARef.current, fullEl = coreFullRef.current, panel = panelRef.current;
    if (!sec || !field) return;
    const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    const rings = [...field.querySelectorAll('[data-ring]')];
    const dots = [...field.querySelectorAll('[data-dot]')];
    const labels = [...field.querySelectorAll('[data-label]')];
    const lp = labels.map(() => ({ x: 0, y: 0 }));

    const apply = (e, txx, tyy, slide, w, panelOp) => {
      field.style.transform = `translateX(${slide.toFixed(1)}px) rotateX(${(-txx).toFixed(2)}deg) rotateY(${tyy.toFixed(2)}deg)`;
      rings.forEach((r, i) => { r.style.transform = `translateZ(${(i + 1) * 36}px) scale(${(0.16 + 0.84 * e).toFixed(3)})`; r.style.opacity = e.toFixed(3); });
      dots.forEach((d) => { d.style.opacity = (e * 0.85).toFixed(3); d.style.transform = `rotate(${d.dataset.a}deg) translateX(${(250 * e).toFixed(1)}px) scale(${e.toFixed(3)})`; });
      labels.forEach((l, i) => {
        const lx = +l.dataset.lx, ly = +l.dataset.ly;
        l.style.transform = `translate(-50%,-50%) translate(${(lx * e).toFixed(1)}px,${(ly * e).toFixed(1)}px) translateZ(${(72 * e).toFixed(1)}px)`;
        const act = S.current.activeIdx;
        l.style.opacity = (act >= 0 && act !== i ? e * 0.2 : e).toFixed(3);
      });
      if (panel) { panel.style.opacity = panelOp.toFixed(3); panel.style.transform = `translateY(-50%) translateX(${((1 - panelOp) * 26).toFixed(1)}px)`; }
    };

    if (reduce) { apply(1, 0, 0, 0, 130, 0); return; }

    const onMove = (e) => { S.current.mx = e.clientX / window.innerWidth - 0.5; S.current.my = e.clientY / window.innerHeight - 0.5; };
    window.addEventListener('mousemove', onMove);

    let raf = 0, cE = 0, cTX = 0, cTY = 0, cSlide = 0, cW = 130, cPanel = 0;
    const loop = () => {
      const r = sec.getBoundingClientRect(), vh = window.innerHeight;
      let prog = 1 - (r.top + r.height * 0.12) / (vh * 0.9);
      prog = Math.max(0, Math.min(1, prog));
      const e = Math.max(0, Math.min(1, (prog - 0.10) / 0.5)); // core-only first, then explode
      cE += (e - cE) * 0.12;
      const tiltMax = 9;                                        // gentle 3D — labels stay put under the cursor
      cTX += (S.current.my * tiltMax * cE - cTX) * 0.08;
      cTY += (S.current.mx * tiltMax * cE - cTY) * 0.08;
      cSlide += ((S.current.activeIdx >= 0 ? -150 : 0) - cSlide) * 0.1;
      cPanel += ((S.current.activeIdx >= 0 ? 1 : 0) - cPanel) * 0.12;
      apply(cE, cTX, cTY, cSlide, cW, cPanel);
      raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop);
    return () => { cancelAnimationFrame(raf); window.removeEventListener('mousemove', onMove); };
  }, [isMobile]);

  const active = activeIdx >= 0 ? ORBIT_ITEMS[activeIdx] : null;

  return (
    <section className="aevorbit" ref={secRef}>
      <div className="aevorbit__aura aevorbit__aura--gold" data-parallax="0.06" aria-hidden="true"></div>
      <div className="aevorbit__aura aevorbit__aura--navy" data-parallax="0.04" aria-hidden="true"></div>
      <div className="aevsite-wrap aevorbit__stage">
        <div className="aevorbit__eyebrow"><Kicker>Every product, accountable</Kicker></div>
        {isMobile ? (
          <div className="aevorbit__mobile">
            <div className="aevorbit__mcore">A</div>
            <ul className="aevorbit__mlist">
              {ORBIT_ITEMS.map((it) => (
                <li key={it.label}>
                  <div className="aevorbit__mk"><span>✓</span>{it.label}</div>
                  <p>{it.body}</p>
                </li>
              ))}
            </ul>
          </div>
        ) : (
        <div className="aevorbit__interactive">
          <div className="aevorbit__field" ref={fieldRef}>
            <div className="aevorbit__ring aevorbit__ring--o" data-ring></div>
            <div className="aevorbit__ring aevorbit__ring--m" data-ring></div>
            <div className="aevorbit__ring aevorbit__ring--i" data-ring></div>
            {ORBIT_DOTS.map((a, i) => (<span key={i} className="aevorbit__dot" data-dot data-a={a}></span>))}
            {ORBIT_ITEMS.map((it, i) => (
              <span key={it.label} className={`aevorbit__label ${activeIdx === i ? 'is-active' : ''}`} data-label data-lx={it.lx} data-ly={it.ly}
                    role="button" tabIndex={0}
                    onClick={() => setActive(activeIdx === i ? -1 : i)}
                    onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setActive(activeIdx === i ? -1 : i); } }}>
                <span className="aevorbit__lcheck">✓</span>{it.label}
              </span>
            ))}
            <div className="aevorbit__core">
              <span className="aevorbit__core-a">A</span>
            </div>
          </div>
          <div className={`aevorbit__panel ${active ? 'is-open' : ''}`} ref={panelRef} aria-live="polite">
            {active && (
              <React.Fragment>
                <button className="aevorbit__panel-x" onClick={() => setActive(-1)} aria-label="Close">×</button>
                <div className="aevorbit__panel-k"><span>✓</span>{active.label}</div>
                <p>{active.body}</p>
              </React.Fragment>
            )}
          </div>
        </div>
        )}
        <div className="aevorbit__cap">
          <h2>One standard, every category.</h2>
          <p>Peptides, supplements, cosmetics, medicines: each unit verified, serialized, and traceable before it earns the name.</p>
        </div>
      </div>
    </section>
  );
}

function Home({ go, addToCart }) {
  const { Kicker } = DSh;
  const [muted, setMuted] = React.useState(true);
  const [drawer, setDrawer] = React.useState(null);   // selected product array, or null
  // B49: live "The collection" = the products most present in member wishlists (daily-rotating
  // ties), TOP 3 shown (owner 2026-07-28: three larger cards whose plate matches the photo ratio).
  // Starts null (loading) so the built-in list NEVER flashes before the real one paints; the
  // fallback is used ONLY when the feed is empty/unavailable, so it still degrades gracefully.
  const [collection, setCollection] = React.useState(null);
  React.useEffect(() => {
    let alive = true;
    fetch('/api/collection')
      .then((r) => r.json())
      .then((d) => {
        if (!alive) return;
        if (d && Array.isArray(d.products) && d.products.length) {
          // B54.2: the medicine leads — headline slot carries the actives with strengths
          // ("Tirzepatide 29 mg"), the small line carries the BRAND. Offline fallback items keep
          // the design's own demo shape (they never reach this mapping).
          setCollection(d.products.slice(0, 3).map((p) => [
            p.activesLabel || p.name,
            p.activesLabel ? p.name : (p.category || p.subcategory || ''),
            p.image,
            p.floorUsd ?? p.priceUsd,
            p.presentation || p.sub || '',
            p.id,
          ]));
        } else {
          setCollection(COLLECTION.slice(0, 3)); // empty feed → offline fallback (same 3-card layout)
        }
      })
      .catch(() => { if (alive) setCollection(COLLECTION.slice(0, 3)); }); // network error → offline fallback
    return () => { alive = false; };
  }, []);
  // More+ on a live product deep-links the shop and opens its modal (window-scoped signal, the same
  // cross-script channel the kit already uses for AEV_ROUTES / scroll-lock). Offline items (id=null)
  // keep the original local drawer so the homepage still previews the fallback set.
  const openCollItem = (it) => {
    const id = it[5];
    if (id) { window.AEV_OPEN_PRODUCT = id; go('shop'); }
    else { setDrawer(it); }
  };
  const lastProd = React.useRef(null);
  if (drawer) lastProd.current = drawer;
  const dp = drawer || lastProd.current;               // persists during exit transition
  React.useEffect(() => {
    if (!drawer) return undefined;
    const onKey = (e) => { if (e.key === 'Escape') setDrawer(null); };
    document.addEventListener('keydown', onKey);
    const lock = window.aevScrollLock;
    if (lock) lock.acquire();
    return () => { document.removeEventListener('keydown', onKey); if (lock) lock.release(); };
  }, [drawer]);
  const vA = React.useRef(null);
  const vB = React.useRef(null);
  const veilRef = React.useRef(null);
  const runRef = React.useRef(null);
  const mutedRef = React.useRef(true);
  // The still photo is the video's first frame. On load the photo shows, the video cross-fades in over
  // it (the photo "comes alive"), plays once, and freezes on its sharp 4K last frame (which matches the
  // photo). Pressing "Sound on" replays the whole thing with audio, then it freezes again and self-mutes.
  React.useEffect(() => {
    const a = vA.current;
    if (!a) return;
    a.loop = false; a.style.zIndex = '2';
    const HOLD = 450, FADE_IN = 1300;
    let raf = 0, phase = 'idle', t0 = 0;
    const set = (o) => { a.style.opacity = Math.max(0, Math.min(1, o)).toFixed(3); };
    const loop = () => {
      const now = performance.now(), dur = a.duration;
      if (phase === 'hold') { set(0); if (now - t0 >= HOLD) phase = 'in'; }
      else if (phase === 'in') { set((now - t0 - HOLD) / FADE_IN); if (now - t0 - HOLD >= FADE_IN) phase = 'playing'; }
      else if (phase === 'playing') {
        set(1);
        if (isFinite(dur) && !a.muted) {
          const AUD = 1.4; // seconds — ramp the audio down over the tail so it isn't cut abruptly
          a.volume = Math.max(0, Math.min(1, (dur - a.currentTime) / AUD));
        }
        if (a.ended || (isFinite(dur) && a.currentTime >= dur - 0.05)) {
          a.pause(); set(1);                                  // freeze on the sharp last frame
          if (!a.muted) { a.muted = true; mutedRef.current = true; setMuted(true); } // sound auto-off
          a.volume = 1;
          phase = 'done';
        }
      }
      if (phase !== 'done') raf = requestAnimationFrame(loop);
    };
    const run = (withSound) => {
      cancelAnimationFrame(raf);
      a.muted = !withSound; mutedRef.current = !withSound; setMuted(!withSound);
      a.volume = 1;
      try { a.currentTime = 0; } catch (e) {}
      set(0); phase = 'hold'; t0 = performance.now();
      const p = a.play();
      if (p && p.catch) p.catch(() => { a.muted = true; mutedRef.current = true; setMuted(true); a.play().catch(() => {}); });
      raf = requestAnimationFrame(loop);
    };
    runRef.current = run;
    run(false);                                               // initial autoplay, muted
    return () => cancelAnimationFrame(raf);
  }, []);
  const toggleMute = () => {
    if (mutedRef.current) {
      // sound is off → turn it on and replay the animation from the start
      if (runRef.current) runRef.current(true);
    } else {
      // sound is on → just mute
      mutedRef.current = true; setMuted(true);
      if (vA.current) vA.current.muted = true;
    }
  };
  return (
    <div>
      {/* HERO */}
      <section className="aevsite-hero2" data-hero>
        <div className="aevsite-hero2__bg" data-hero-bg aria-hidden="true">
          <img className="aevsite-hero2__still" src="../../assets/hero-still-4k.jpg" alt="" />
          <video ref={vA} className="aevsite-hero2__vid" src="../../assets/hero-longevity-3.mp4" preload="auto" loop={false} playsInline></video>
        </div>
        <div className="aevsite-hero2__fog" aria-hidden="true"></div>
        <button className="aevsite-hero2__sound" onClick={toggleMute} aria-label={muted ? 'Turn sound on' : 'Turn sound off'}>
          {muted ? (
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><path d="M11 5 6 9H2v6h4l5 4V5Z"/><path d="m23 9-6 6"/><path d="m17 9 6 6"/></svg>
          ) : (
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><path d="M11 5 6 9H2v6h4l5 4V5Z"/><path d="M15.5 8.5a5 5 0 0 1 0 7"/><path d="M19 5a9 9 0 0 1 0 14"/></svg>
          )}
          <span>{muted ? 'Sound off' : 'Sound on'}</span>
        </button>
        <div className="aevsite-wrap aevsite-hero2__content">
          <div className="aevsite-hero2__eyebrow" data-parallax="0.05"><Kicker>Verified longevity</Kicker></div>
          <h1 className="aevsite-hero2__title" data-parallax="0.12">Live<br />well,<br />longer.</h1>
          <div className="aevsite-hero2__scroll">Scroll</div>
          <div className="aevsite-hero2__aside" data-parallax="0.08">
            <p>We curate longevity through rigorous scientific evaluation to support health professionals in promoting longevity and a better quality of life for the people they care for.</p>
            <a className="aev-ul" href={RP_HOME.science} onClick={(e) => { e.preventDefault(); go('science'); }}>Explore our science</a>
          </div>
        </div>
      </section>

      {/* spacer so the fixed hero shows for one screen, then flow scrolls over it */}
      <div className="aevsite-hero-spacer" aria-hidden="true"></div>
      {/* Everything below slides up over the fixed hero */}
      <div className="aevsite-rise">
      {/* ORBIT SIGNATURE */}
      <OrbitRing />

      {/* THE COLLECTION */}
      <section className="aevsite-band">
        <div className="aevsite-wrap aevsite-reveal">
          <div className="aevsite-sec-head">
            <h2 className="aevsite-h2"><Mask>The collection</Mask></h2>
            <a className="aevsite-seeall aev-ul" href={RP_HOME.shop} onClick={(e) => { e.preventDefault(); go('shop'); }}>View all →</a>
          </div>
          <div className="aevsite-coll">
            {(collection || []).map(([name, cat, img], i) => (
              <figure key={name} className="aevsite-coll__it" style={{ ['--i']: i }} onClick={() => go('shop')}>
                <div className="aevsite-coll__plate"><img src={img} alt={name} /></div>
                <figcaption>
                  <div className="aevsite-coll__cat">{cat}</div>
                  <div className="aevsite-coll__name">{name}</div>
                  <button className="aevsite-coll__add" onClick={(e) => { e.stopPropagation(); openCollItem(collection[i]); }}>More +</button>
                </figcaption>
              </figure>
            ))}
          </div>
        </div>
      </section>

      {/* OUR PHILOSOPHY */}
      <section className="aevsite-band aevsite-mist aevsite-philo">
        <div className="aevsite-wrap">
          <div data-philo-item><Kicker>Our philosophy</Kicker></div>
          <h2 className="aevsite-h2 aevsite-philo__h" data-philo-item>Curation is the product.</h2>
          <p className="aevsite-philo__lead" data-philo-item>Our curation process is led by a team of experts comprising of physicians, toxicologists, former regulators and nutritionists.</p>
          <div className="aevsite-philo__grid">
            {PILLARS.map(([t, d], i) => (
              <div key={t} className="aevsite-philo__it" data-philo-item>
                <span className="aevsite-philo__n">{String(i + 1).padStart(2, '0')}</span>
                <h3 className="aevsite-philo__t">{t}</h3>
                <p className="muted">{d}</p>
              </div>
            ))}
          </div>
          <a className="aevsite-philo__link aev-ul" data-philo-item href={RP_HOME.science} onClick={(e) => { e.preventDefault(); go('science'); }}>Explore our science →</a>
        </div>
      </section>

      {/* LONGEVITY CLUB */}
      <section className="aevsite-band aevsite-mist">
        <div className="aevsite-wrap aevsite-reveal">
          <div className="aevsite-sec-head">
            <div>
              <Kicker>Aevitan Longevity Club</Kicker>
              <h2 className="aevsite-h2" style={{ marginTop: 6 }}>One membership. Benefits that grow.</h2>
            </div>
            <a className="aevsite-seeall aev-ul" href={RP_HOME.club} onClick={(e) => { e.preventDefault(); go('club'); }}>View the program →</a>
          </div>
          <p className="aevsite-philo__lead" style={{ marginBottom: 34 }}>Membership is common and free. Your member type shapes the profile information and declaration relevant to you; Club standing and the professional curation reports grow from there.</p>
          <div className="aevpath">
            {CLUB_PATHS.map(([who, name, desc], i) => (
              <div key={name} className={`aevpath__card ${i === 2 ? 'aevpath__card--feature' : ''}`}>
                <div className="aevpath__who">{who}</div>
                <div className="aevpath__name">{name}</div>
                <p className="aevpath__desc">{desc}</p>
              </div>
            ))}
          </div>
        </div>
      </section>
      </div>

      {/* PRODUCT DRAWER */}
      <div className={`aevdraw__scrim ${drawer ? 'is-open' : ''}`} onClick={() => setDrawer(null)} aria-hidden={!drawer}></div>
      <aside className={`aevdraw ${drawer ? 'is-open' : ''}`} role="dialog" aria-modal="true" aria-hidden={!drawer}>
        <button className="aevdraw__close" onClick={() => setDrawer(null)} aria-label="Close">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"><path d="M18 6 6 18M6 6l12 12"/></svg>
        </button>
        {dp && (
          <React.Fragment>
            <div className="aevdraw__plate"><img src={dp[2]} alt={dp[0]} /></div>
            <div className="aevdraw__body">
              <div className="aevdraw__cat">{dp[1]}</div>
              <h3 className="aevdraw__name">{dp[0]}</h3>
              <p className="aevdraw__desc">{dp[4]}</p>
              <div className="aevdraw__price">
                <span className="aevdraw__price-label">From</span>
                <span className="aevdraw__price-val">${dp[3]}</span>
                <span className="aevdraw__price-label">USD</span>
              </div>
              <button className="aevdraw__cta" onClick={() => { window.location.href = PORTAL_URL + '/login'; }}>Sign in to add to cart</button>
              <a className="aevdraw__link aev-ul" onClick={() => { setDrawer(null); go('shop'); }}>View full details →</a>
            </div>
          </React.Fragment>
        )}
      </aside>
    </div>
  );
}

window.Home = Home;
