// Shared components used across the three directions.
// Each direction restyles them via inline style props or wrapper classes.

// ─────────────────────────────────────────────────────────────
// useScrollReveal — fades + slides element in once it enters its scroll
// container (the artboard's .twj-page). Uses IntersectionObserver scoped
// to the closest scroll ancestor.
function useScrollReveal({ delay = 0, y = 24 } = {}) {
  const ref = React.useRef(null);
  const [shown, setShown] = React.useState(false);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let root = el.parentElement;
    while (root && root !== document.body) {
      const oy = getComputedStyle(root).overflowY;
      if (oy === 'auto' || oy === 'scroll') break;
      root = root.parentElement;
    }
    const io = new IntersectionObserver(
      (entries) => entries.forEach((e) => { if (e.isIntersecting) setShown(true); }),
      { root: root && root !== document.body ? root : null, threshold: 0.12, rootMargin: '0px 0px -40px 0px' }
    );
    io.observe(el);
    return () => io.disconnect();
  }, []);
  const style = {
    transition: `opacity .8s cubic-bezier(.2,.7,.3,1) ${delay}ms, transform .9s cubic-bezier(.2,.7,.3,1) ${delay}ms`,
    opacity: shown ? 1 : 0,
    transform: shown ? 'translateY(0)' : `translateY(${y}px)`,
  };
  return [ref, style];
}

function Reveal({ children, delay = 0, y = 24, as = 'div', style: extra, ...rest }) {
  const [ref, rs] = useScrollReveal({ delay, y });
  const Tag = as;
  return <Tag ref={ref} style={{ ...rs, ...extra }} {...rest}>{children}</Tag>;
}

// ─────────────────────────────────────────────────────────────
// Image slot — wraps the <image-slot> web component with sensible defaults
function Img({ id, w, h, label, shape = 'rect', radius = 0, style, className }) {
  return (
    <image-slot
      id={`twj-${id}`}
      shape={shape}
      radius={radius}
      placeholder={label || 'Drop photo'}
      style={{ display: 'block', width: w, height: h, ...style }}
      class={className}
    />
  );
}

// ─────────────────────────────────────────────────────────────
// Before/After slider with draggable handle
function BeforeAfter({ height = 360, beforeLabel = 'Before', afterLabel = 'After', accent = '#C66E47', radius = 0, ink = '#1F1410' }) {
  const [pct, setPct] = React.useState(50);
  const wrapRef = React.useRef(null);
  const dragging = React.useRef(false);

  const setFromX = (clientX) => {
    const r = wrapRef.current?.getBoundingClientRect();
    if (!r) return;
    const x = Math.max(0, Math.min(r.width, clientX - r.left));
    setPct((x / r.width) * 100);
  };
  const onDown = (e) => { dragging.current = true; setFromX(e.clientX ?? e.touches?.[0].clientX); e.preventDefault(); };
  React.useEffect(() => {
    const move = (e) => { if (!dragging.current) return; setFromX(e.clientX ?? e.touches?.[0].clientX); };
    const up = () => { dragging.current = false; };
    window.addEventListener('pointermove', move);
    window.addEventListener('pointerup', up);
    return () => { window.removeEventListener('pointermove', move); window.removeEventListener('pointerup', up); };
  }, []);

  return (
    <div ref={wrapRef} onPointerDown={onDown} style={{ position: 'relative', width: '100%', height, borderRadius: radius, overflow: 'hidden', cursor: 'ew-resize', userSelect: 'none', background: '#e9ddd0' }}>
      {/* AFTER (full underlay) */}
      <div style={{ position: 'absolute', inset: 0 }}>
        <Img id="ba-after" w="100%" h="100%" label="After photo" />
        <span style={ribbonStyle(afterLabel, 'right', accent, ink)}>{afterLabel}</span>
      </div>
      {/* BEFORE (clipped) */}
      <div style={{ position: 'absolute', inset: 0, clipPath: `inset(0 ${100 - pct}% 0 0)` }}>
        <Img id="ba-before" w="100%" h="100%" label="Before photo" style={{ background: '#d8c8b6' }} />
        <span style={ribbonStyle(beforeLabel, 'left', '#3d2818', '#fff')}>{beforeLabel}</span>
      </div>
      {/* Handle */}
      <div style={{ position: 'absolute', top: 0, bottom: 0, left: `${pct}%`, width: 2, background: '#fff', boxShadow: '0 0 0 1px rgba(0,0,0,.15)', transform: 'translateX(-50%)', pointerEvents: 'none' }} />
      <div style={{ position: 'absolute', top: '50%', left: `${pct}%`, transform: 'translate(-50%,-50%)', width: 44, height: 44, borderRadius: 999, background: '#fff', boxShadow: '0 4px 14px rgba(0,0,0,.18)', display: 'grid', placeItems: 'center', pointerEvents: 'none' }}>
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke={ink} strokeWidth="2" strokeLinecap="round">
          <path d="M9 6l-4 6 4 6M15 6l4 6-4 6" />
        </svg>
      </div>
    </div>
  );
}
function ribbonStyle(_, side, bg, fg) {
  return {
    position: 'absolute', top: 16, [side]: 16,
    background: bg, color: fg, fontSize: 11, letterSpacing: '0.18em', textTransform: 'uppercase',
    padding: '6px 10px', borderRadius: 999, fontWeight: 600,
  };
}

// ─────────────────────────────────────────────────────────────
// Auto-rotating testimonial carousel
const TESTIMONIALS = [
  { name: 'Linda · 62', from: 'Tucson, AZ', text: 'I started Train With Joan a year ago and I have never felt this strong. The workouts meet me where I am, and the meal plan finally clicked for me.', stat: '34 lbs · 12 months' },
  { name: 'Maria · 58', from: 'Madrid, ES', text: 'After menopause I thought my body was just done. Joan and Michelle proved me wrong. I am the strongest I have ever been at any age.', stat: 'Off blood pressure meds' },
  { name: 'Patricia · 71', from: 'Portland, OR', text: 'I can play with my grandchildren on the floor and get back up. That was my goal. The community keeps me showing up every single day.', stat: '8 months strong' },
  { name: 'Cheryl · 55', from: 'Toronto, ON', text: 'The mobility track was the gateway. I could barely do a squat. Now I deadlift 95 lbs and walk pain-free. I cried the first time.', stat: 'Pain-free knees' },
];

function TestimonialRotator({ accent = '#C66E47', ink = '#1F1410', cream = '#F4ECE1', interval = 5000 }) {
  const [i, setI] = React.useState(0);
  const [paused, setPaused] = React.useState(false);
  React.useEffect(() => {
    if (paused) return;
    const t = setInterval(() => setI((x) => (x + 1) % TESTIMONIALS.length), interval);
    return () => clearInterval(t);
  }, [paused, interval]);
  const t = TESTIMONIALS[i];
  return (
    <div onMouseEnter={() => setPaused(true)} onMouseLeave={() => setPaused(false)} style={{ position: 'relative' }}>
      <div style={{ minHeight: 220, display: 'grid', gridTemplateColumns: '120px 1fr', gap: 32, alignItems: 'center' }}>
        <Img id={`testi-${i}`} w={120} h={120} shape="circle" label="member photo" />
        <div>
          <div key={i} style={{ animation: 'twjFade .6s ease-out' }}>
            <div style={{ fontSize: 22, lineHeight: 1.45, color: ink, fontFamily: 'Fraunces, serif', fontStyle: 'italic', marginBottom: 18 }}>
              “{t.text}”
            </div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 16, flexWrap: 'wrap' }}>
              <span style={{ fontWeight: 600, color: ink }}>{t.name}</span>
              <span style={{ fontSize: 13, color: ink, opacity: 0.6 }}>{t.from}</span>
              <span style={{ fontSize: 12, padding: '4px 10px', borderRadius: 999, background: accent, color: cream, letterSpacing: '0.06em', fontWeight: 600 }}>{t.stat}</span>
            </div>
          </div>
        </div>
      </div>
      <div style={{ display: 'flex', gap: 8, marginTop: 28 }}>
        {TESTIMONIALS.map((_, idx) => (
          <button key={idx} onClick={() => setI(idx)} aria-label={`Member ${idx + 1}`} style={{ border: 0, padding: 0, cursor: 'pointer', width: idx === i ? 32 : 10, height: 10, borderRadius: 999, background: idx === i ? accent : 'rgba(0,0,0,0.15)', transition: 'all .3s' }} />
        ))}
      </div>
      <style>{`@keyframes twjFade{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}`}</style>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// FAQ accordion
const FAQS = [
  { q: 'Do I need a gym or special equipment?', a: 'No gym needed. You can do every workout at home with a set of dumbbells and resistance bands. We also have a limited-mobility track that requires no equipment at all.' },
  { q: "I'm in my 60s — is this really for me?", a: 'Yes. Joan started at 70 and the app was built around women in their 50s, 60s, and 70s. Workouts scale to where you are today, and there is a modified track for limited mobility.' },
  { q: 'How long are the workouts?', a: 'Most workouts run 30 to 45 minutes, three to five days a week. They are fully guided so you press play and follow along — no planning, no guessing.' },
  { q: 'What does the meal plan look like?', a: 'Real food, generous portions, no extreme restrictions. You get weekly menus, swappable recipes, and grocery lists. Everything is built to be sustainable for life.' },
  { q: 'Can I cancel anytime?', a: "Yes. The free trial is genuinely free, and you can cancel your monthly or annual subscription anytime from your account settings." },
  { q: 'Is there real support if I get stuck?', a: 'Yes. The in-app community is active every day, with Joan, Michelle, and certified coaches answering questions, demoing form, and cheering you on.' },
];

function FAQAccordion({ accent = '#C66E47', ink = '#1F1410', divider = 'rgba(31,20,16,0.12)' }) {
  const [open, setOpen] = React.useState(0);
  return (
    <div>
      {FAQS.map((f, idx) => {
        const isOpen = open === idx;
        return (
          <div key={idx} style={{ borderTop: `1px solid ${divider}`, ...(idx === FAQS.length - 1 ? { borderBottom: `1px solid ${divider}` } : {}) }}>
            <button
              onClick={() => setOpen(isOpen ? -1 : idx)}
              style={{ width: '100%', textAlign: 'left', background: 'transparent', border: 0, padding: '24px 0', display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 24, cursor: 'pointer', color: ink, fontFamily: 'inherit', fontSize: 19, fontWeight: 500 }}
            >
              <span>{f.q}</span>
              <span style={{ flexShrink: 0, width: 36, height: 36, borderRadius: 999, border: `1px solid ${divider}`, display: 'grid', placeItems: 'center', transition: 'all .3s', background: isOpen ? accent : 'transparent', color: isOpen ? '#fff' : ink }}>
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" style={{ transform: isOpen ? 'rotate(45deg)' : 'rotate(0)', transition: 'transform .3s' }}>
                  <path d="M12 5v14M5 12h14" />
                </svg>
              </span>
            </button>
            <div style={{ maxHeight: isOpen ? 240 : 0, overflow: 'hidden', transition: 'max-height .45s cubic-bezier(.2,.7,.3,1)' }}>
              <p style={{ margin: 0, padding: '0 0 24px 0', maxWidth: '80%', fontSize: 16, lineHeight: 1.65, color: ink, opacity: 0.78 }}>{f.a}</p>
            </div>
          </div>
        );
      })}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Animated phone mockup — cycles through 'screens'
const PHONE_SCREENS = [
  { tag: 'WORKOUTS', title: "Today's Workout", body: 'Lower Body · 32 min', ctaLabel: 'Press play' },
  { tag: 'MEAL PLAN', title: 'This Week', body: '21 meals · grocery list ready', ctaLabel: 'View plan' },
  { tag: 'PROGRESS', title: '47-day streak', body: '+8 lbs lifted · –2 inches', ctaLabel: 'See trend' },
  { tag: 'COMMUNITY', title: 'Live with Joan', body: '286 women showing up today', ctaLabel: 'Join in' },
];

function PhoneMockup({ accent = '#C66E47', ink = '#1F1410', cream = '#F4ECE1', interval = 3200, scale = 1 }) {
  const [i, setI] = React.useState(0);
  React.useEffect(() => {
    const t = setInterval(() => setI((x) => (x + 1) % PHONE_SCREENS.length), interval);
    return () => clearInterval(t);
  }, [interval]);
  const s = PHONE_SCREENS[i];
  const W = 280 * scale, H = 580 * scale;
  return (
    <div style={{ width: W, height: H, position: 'relative', filter: 'drop-shadow(0 30px 60px rgba(0,0,0,0.18))' }}>
      <div style={{ width: '100%', height: '100%', background: '#15100c', borderRadius: 44 * scale, padding: 8 * scale, position: 'relative' }}>
        <div style={{ width: '100%', height: '100%', background: cream, borderRadius: 38 * scale, overflow: 'hidden', position: 'relative', display: 'flex', flexDirection: 'column' }}>
          {/* notch */}
          <div style={{ position: 'absolute', top: 8 * scale, left: '50%', transform: 'translateX(-50%)', width: 96 * scale, height: 22 * scale, background: '#15100c', borderRadius: 999 }} />
          {/* status bar */}
          <div style={{ display: 'flex', justifyContent: 'space-between', padding: `${14 * scale}px ${22 * scale}px ${30 * scale}px`, fontSize: 11 * scale, color: ink, fontWeight: 600 }}>
            <span>9:41</span>
            <span style={{ display: 'flex', gap: 4, alignItems: 'center' }}>•••••</span>
          </div>
          {/* hero photo */}
          <div style={{ margin: `0 ${16 * scale}px`, height: 200 * scale, background: '#e9ddd0', borderRadius: 24 * scale, position: 'relative', overflow: 'hidden' }}>
            <Img id={`phone-${i}`} w="100%" h="100%" label={s.tag.toLowerCase()} />
            <div style={{ position: 'absolute', top: 12 * scale, left: 12 * scale, padding: '4px 9px', background: accent, color: cream, fontSize: 9 * scale, letterSpacing: '0.18em', fontWeight: 700, borderRadius: 999 }}>{s.tag}</div>
          </div>
          {/* content card */}
          <div key={i} style={{ flex: 1, padding: `${20 * scale}px ${22 * scale}px`, animation: 'twjPhoneSwap .55s ease-out' }}>
            <div style={{ fontFamily: 'Fraunces, serif', fontSize: 24 * scale, color: ink, lineHeight: 1.15 }}>{s.title}</div>
            <div style={{ fontSize: 13 * scale, color: ink, opacity: 0.6, marginTop: 6 * scale }}>{s.body}</div>
            <div style={{ marginTop: 24 * scale, display: 'flex', alignItems: 'center', gap: 10 }}>
              <div style={{ flex: 1, height: 6 * scale, background: 'rgba(0,0,0,0.07)', borderRadius: 999, overflow: 'hidden' }}>
                <div style={{ width: `${30 + i * 18}%`, height: '100%', background: accent, transition: 'width .8s' }} />
              </div>
              <span style={{ fontSize: 11 * scale, color: ink, opacity: 0.5 }}>day {i + 1}</span>
            </div>
            <button style={{ marginTop: 24 * scale, width: '100%', padding: `${14 * scale}px`, background: ink, color: cream, border: 0, borderRadius: 14 * scale, fontWeight: 600, fontSize: 13 * scale, fontFamily: 'inherit', cursor: 'pointer' }}>{s.ctaLabel}</button>
          </div>
          {/* tab bar */}
          <div style={{ display: 'flex', justifyContent: 'space-around', padding: `${10 * scale}px 0 ${18 * scale}px`, borderTop: '1px solid rgba(0,0,0,0.06)' }}>
            {['Train', 'Eat', 'You', 'Community'].map((label, idx) => (
              <span key={label} style={{ fontSize: 10 * scale, color: idx === i ? accent : 'rgba(0,0,0,0.4)', fontWeight: 600 }}>{label}</span>
            ))}
          </div>
          {/* dots */}
          <div style={{ position: 'absolute', bottom: 6 * scale, left: '50%', transform: 'translateX(-50%)', display: 'flex', gap: 4 }}>
            {PHONE_SCREENS.map((_, idx) => (
              <div key={idx} style={{ width: idx === i ? 14 * scale : 4 * scale, height: 4 * scale, borderRadius: 999, background: idx === i ? accent : 'rgba(0,0,0,0.18)', transition: 'all .3s' }} />
            ))}
          </div>
        </div>
      </div>
      <style>{`@keyframes twjPhoneSwap{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}`}</style>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Real member transformation gallery — uses uploaded before/after photos.
// Each photo is itself a side-by-side composite, so we just render the
// image full-bleed with a small ribbon. 8 members supplied.
const REAL_MEMBERS = [
  { src: 'assets/members/m1.png', name: 'Dana', age: 54, stat: 'Stronger every quarter' },
  { src: 'assets/members/m2.png', name: 'Susan', age: 61, stat: '–28 lbs · 9 months' },
  { src: 'assets/members/m3.png', name: 'Helen', age: 66, stat: 'Mobility back · pain-free' },
  { src: 'assets/members/m4.png', name: 'Rachel', age: 42, stat: '–18 lbs · 6 months' },
  { src: 'assets/members/m5.png', name: 'Karen', age: 58, stat: 'First deadlift at 58' },
  { src: 'assets/members/m6.png', name: 'Beth', age: 49, stat: '12 weeks consistent' },
  { src: 'assets/members/m7.png', name: 'Janet', age: 64, stat: 'Off blood pressure meds' },
  { src: 'assets/members/m8.png', name: 'Linda', age: 71, stat: 'Strongest at 71' },
];

function MembersGrid({ accent = '#FB8A99', ink = '#1F2128', ink2 = 'rgba(31,33,40,0.6)', cols = 4, radius = 20, ribbonStyle = 'pill', items = REAL_MEMBERS }) {
  return (
    <div style={{ display: 'grid', gridTemplateColumns: `repeat(${cols}, 1fr)`, gap: 20 }}>
      {items.map((m, i) => (
        <Reveal key={m.src} delay={i * 50}>
          <figure style={{ margin: 0, position: 'relative', borderRadius: radius, overflow: 'hidden', background: '#eee', aspectRatio: '1 / 1' }}>
            <img src={m.src} alt={`${m.name} before & after`} style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
            <span style={{ position: 'absolute', top: 12, left: 12, background: 'rgba(31,33,40,0.85)', color: '#fff', padding: '4px 9px', borderRadius: 999, fontSize: 9, letterSpacing: '0.18em', textTransform: 'uppercase', fontWeight: 700 }}>Before</span>
            <span style={{ position: 'absolute', top: 12, right: 12, background: accent, color: '#fff', padding: '4px 9px', borderRadius: 999, fontSize: 9, letterSpacing: '0.18em', textTransform: 'uppercase', fontWeight: 700 }}>After</span>
            <figcaption style={{ position: 'absolute', bottom: 0, left: 0, right: 0, padding: '40px 14px 14px', background: 'linear-gradient(180deg, transparent, rgba(0,0,0,0.78))', color: '#fff' }}>
              <div style={{ fontFamily: 'Bricolage Grotesque, sans-serif', fontSize: 18, fontWeight: 600, letterSpacing: '-0.01em' }}>{m.name}, {m.age}</div>
              <div style={{ fontSize: 12, opacity: 0.85, marginTop: 2 }}>{m.stat}</div>
            </figcaption>
          </figure>
        </Reveal>
      ))}
    </div>
  );
}

// Marquee strip variant — for full-bleed placement on V3
function MembersStrip({ accent = '#FB8A99', items = REAL_MEMBERS, height = 280 }) {
  return (
    <div style={{ display: 'flex', gap: 16, overflowX: 'auto', paddingBottom: 12, scrollSnapType: 'x mandatory' }}>
      {items.map((m) => (
        <figure key={m.src} style={{ margin: 0, flex: '0 0 auto', width: height, height, borderRadius: 18, overflow: 'hidden', position: 'relative', background: '#eee', scrollSnapAlign: 'start' }}>
          <img src={m.src} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
          <span style={{ position: 'absolute', top: 10, right: 10, background: accent, color: '#fff', padding: '3px 8px', borderRadius: 999, fontSize: 9, letterSpacing: '0.16em', fontWeight: 700, textTransform: 'uppercase' }}>After</span>
          <figcaption style={{ position: 'absolute', bottom: 0, left: 0, right: 0, padding: '32px 12px 12px', background: 'linear-gradient(180deg, transparent, rgba(0,0,0,0.8))', color: '#fff' }}>
            <div style={{ fontFamily: 'Bricolage Grotesque, sans-serif', fontSize: 15, fontWeight: 600 }}>{m.name}, {m.age}</div>
            <div style={{ fontSize: 11, opacity: 0.85 }}>{m.stat}</div>
          </figcaption>
        </figure>
      ))}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// App store badges — link out to the real Train With Joan listings
const APP_STORE_URL = 'https://apps.apple.com/us/app/train-with-joan/id1576226138';
const PLAY_STORE_URL = 'https://play.google.com/store/apps/details?id=com.influencers.trainwithjoan&hl=en';

function AppStoreBadges({ height = 52, dark = true, gap = 12, style }) {
  const bg = dark ? '#1F2128' : '#FFFFFF';
  const fg = dark ? '#FFFFFF' : '#1F2128';
  const border = dark ? 'transparent' : 'rgba(31,33,40,0.12)';
  const sub = dark ? 'rgba(255,255,255,0.75)' : 'rgba(31,33,40,0.65)';
  const w = Math.round(height * 3.1);
  const px = Math.max(12, Math.round(height * 0.28));
  return (
    <div style={{ display: 'flex', gap, flexWrap: 'wrap', ...style }}>
      <a href={APP_STORE_URL} target="_blank" rel="noopener noreferrer" aria-label="Download on the App Store"
        style={{ display: 'inline-flex', alignItems: 'center', gap: 12, height, width: w, padding: `0 ${px}px`, background: bg, color: fg, border: `1px solid ${border}`, borderRadius: 14, textDecoration: 'none', boxSizing: 'border-box' }}>
        <svg width={Math.round(height * 0.5)} height={Math.round(height * 0.5)} viewBox="0 0 24 24" fill={fg}>
          <path d="M17.05 12.74c-.02-2.3 1.88-3.4 1.96-3.46-1.07-1.56-2.73-1.78-3.32-1.8-1.41-.14-2.76.83-3.48.83-.72 0-1.83-.81-3.01-.79-1.55.02-2.98.9-3.78 2.29-1.61 2.79-.41 6.92 1.16 9.18.77 1.11 1.69 2.36 2.9 2.31 1.16-.05 1.6-.75 3.01-.75 1.4 0 1.8.75 3.03.72 1.25-.02 2.04-1.13 2.81-2.24.88-1.29 1.24-2.54 1.26-2.6-.03-.01-2.42-.93-2.45-3.69zM14.78 5.95c.64-.78 1.07-1.85.95-2.95-.92.04-2.04.61-2.7 1.39-.59.69-1.12 1.81-.98 2.86 1.03.08 2.09-.52 2.73-1.3z" />
        </svg>
        <span style={{ display: 'flex', flexDirection: 'column', lineHeight: 1, fontFamily: '-apple-system, system-ui, sans-serif' }}>
          <span style={{ fontSize: Math.round(height * 0.2), opacity: 0.8, color: sub, marginBottom: 3 }}>Download on the</span>
          <span style={{ fontSize: Math.round(height * 0.34), fontWeight: 600, letterSpacing: '-0.01em' }}>App Store</span>
        </span>
      </a>
      <a href={PLAY_STORE_URL} target="_blank" rel="noopener noreferrer" aria-label="Get it on Google Play"
        style={{ display: 'inline-flex', alignItems: 'center', gap: 12, height, width: w, padding: `0 ${px}px`, background: bg, color: fg, border: `1px solid ${border}`, borderRadius: 14, textDecoration: 'none', boxSizing: 'border-box' }}>
        <svg width={Math.round(height * 0.5)} height={Math.round(height * 0.5)} viewBox="0 0 24 24">
          <path d="M3.6 1.4C3.2 1.7 3 2.2 3 2.9v18.2c0 .7.2 1.2.6 1.5l11.3-11.3L3.6 1.4z" fill="#00D7B5" />
          <path d="M17.6 8.7L14.9 7.2 12 10.1l3.6 3.6 2-1.1c1.6-.9 1.6-3.1 0-4z" fill="#FFCE00" />
          <path d="M3.6 22.6c.4.3 1 .3 1.7-.1l9.6-5.5L12 14.1 3.6 22.6z" fill="#F2384C" />
          <path d="M5.3 1.5c-.7-.4-1.3-.4-1.7-.1L12 10.1l3-3-9.7-5.6z" fill="#0EA8FF" />
        </svg>
        <span style={{ display: 'flex', flexDirection: 'column', lineHeight: 1, fontFamily: 'system-ui, sans-serif' }}>
          <span style={{ fontSize: Math.round(height * 0.2), opacity: 0.8, color: sub, marginBottom: 3 }}>GET IT ON</span>
          <span style={{ fontSize: Math.round(height * 0.34), fontWeight: 600, letterSpacing: '-0.01em' }}>Google Play</span>
        </span>
      </a>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Press logos
const PRESS = ['VOGUE', 'FORBES', 'PEOPLE', 'TODAY', 'WOMEN\'S HEALTH', 'CNN'];

// ─────────────────────────────────────────────────────────────
// Sticky nav helper — uses scroll within the artboard's .twj-page container
function StickyNav({ children, bg, ink, accent, scrolled }) {
  return (
    <div style={{
      position: 'sticky', top: 0, zIndex: 50,
      background: scrolled ? bg : 'transparent',
      backdropFilter: scrolled ? 'blur(12px)' : 'none',
      borderBottom: scrolled ? `1px solid rgba(0,0,0,0.06)` : '1px solid transparent',
      transition: 'background .3s, border-color .3s',
    }}>{children}</div>
  );
}

function useScrolled(threshold = 40) {
  const [scrolled, setScrolled] = React.useState(false);
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let scroller = el;
    while (scroller && scroller !== document.body) {
      const oy = getComputedStyle(scroller).overflowY;
      if (oy === 'auto' || oy === 'scroll') break;
      scroller = scroller.parentElement;
    }
    if (!scroller) return;
    const onScroll = () => setScrolled(scroller.scrollTop > threshold);
    scroller.addEventListener('scroll', onScroll, { passive: true });
    onScroll();
    return () => scroller.removeEventListener('scroll', onScroll);
  }, [threshold]);
  return [ref, scrolled];
}

// Export to window for cross-script use
Object.assign(window, {
  Reveal, useScrollReveal, Img, BeforeAfter, TestimonialRotator, FAQAccordion,
  PhoneMockup, PRESS, StickyNav, useScrolled, TESTIMONIALS, FAQS,
  MembersGrid, MembersStrip, REAL_MEMBERS,
  AppStoreBadges, APP_STORE_URL, PLAY_STORE_URL,
});
