// Page components

function HomePage({ tweaks }) {
  const featured = window.VELO_CONTENT.episodes[0];
  const posts = window.VELO_CONTENT.posts.slice(0, 4);
  const gear = window.VELO_CONTENT.gear.slice(0, 6);
  // Placeholder tiles, shown until/unless the live Instagram feed loads.
  const igFallback = [
    'assets/gravel-road.jpg',
    'assets/cyclist.jpg',
    'assets/gravel-road.jpg',
    'assets/cyclist.jpg',
    'assets/gravel-road.jpg',
    'assets/cyclist.jpg'
  ].map((src, i) => ({ id: 'ph-' + i, image: src, permalink: 'https://www.instagram.com/thegravelloop/' }));

  const [igPosts, setIgPosts] = React.useState(null);
  React.useEffect(() => {
    let live = true;
    fetch('/api/instagram')
      .then(r => (r.ok ? r.json() : null))
      .then(d => { if (live && d && Array.isArray(d.items) && d.items.length) setIgPosts(d.items.slice(0, 6)); })
      .catch(() => {});
    return () => { live = false; };
  }, []);
  const igTiles = igPosts || igFallback;

  return (
    <>
      <Hero heroLayout={tweaks.heroLayout} episode={featured} />

      {/* Featured podcast */}
      <section className="section">
        <div className="wrap">
          <SectionHead
            eyebrow={`Now playing · Episode ${featured.number}`}
            title="The conversations behind the sport."
            link="/podcast"
            linkLabel="All episodes"
          />
          <div className="episode-feature">
            <div className="episode-art" style={{ '--art-image': `url(assets/gravel-road.jpg)` }}>
              <div className="art-inner" style={{ backgroundImage: `url(assets/gravel-road.jpg)` }} />
              <div className="art-label">VH · {featured.number}</div>
              <div className="art-num">{featured.number}</div>
            </div>
            <div>
              <div className="eyebrow" style={{ marginBottom: 16 }}>With {featured.guest} · {featured.duration}</div>
              <h3>{featured.title}</h3>
              <p className="lede">{featured.excerpt}</p>
              <Link to={'/podcast/' + featured.id} className="btn btn-primary" style={{ marginBottom: 40 }}>
                Listen to the full episode <span className="arrow">→</span>
              </Link>
              <div>
                {window.VELO_CONTENT.episodes.map((ep, i) => (
                  <Link key={ep.id} to={'/podcast/' + ep.id} className="episode-row">
                    <span className="n">{ep.number}</span>
                    <span className="t">{ep.title}</span>
                    <span className="d">{ep.duration}</span>
                  </Link>
                ))}
              </div>
            </div>
          </div>
        </div>
      </section>

      {/* Gear grid */}
      <section className="section" style={{ background: 'var(--paper)' }}>
        <div className="wrap">
          <SectionHead eyebrow="Honest gear · From The Gravel Loop" title="Reviews, with no affiliate agenda." link="/newsletter" linkLabel="The Gravel Loop →" />
          <div className="gear-grid">
            {gear.map(g => (
              <Link className="gear-card" key={g.id} to={'/journal/' + g.id}>
                {g.img
                  ? <div className="gear-img" style={{ backgroundImage: `url(${g.img})` }} />
                  : <div className="gear-img placeholder"><span>product shot · {g.cat.toLowerCase()}</span></div>}
                <div className="gear-meta">
                  <span>{g.cat}</span>
                  <span className="rating">{g.rating}</span>
                </div>
                <h4>{g.title}</h4>
                <div className="gear-excerpt">{g.excerpt}</div>
              </Link>
            ))}
          </div>
        </div>
      </section>

      {/* Newsletter */}
      <NewsletterBlock />

      {/* About strip */}
      <section className="section">
        <div className="wrap">
          <div className="about-block">
            <div className="about-img" style={{ backgroundImage: 'url(assets/cyclist.jpg)' }} />
            <div>
              <div className="eyebrow" style={{ marginBottom: 16 }}>Who rides this</div>
              <h2>A rider. A writer. A founder.</h2>
              <p>
                Mike is a gravel rider based in Toronto who got obsessed with the sport a few years ago and never quite recovered. He started Velo Health to tell the kinds of stories, and review the kinds of gear, that he wished he could find as a listener.
              </p>
              <p>
                Velo Health is independent. No parent company, no sponsor pressure on what we cover. Just a point of view, built slowly, with long rides and long conversations. If there are ever sponsors or affiliate links, they'll be disclosed very clearly.
              </p>
              <div className="sig">— Mike</div>
              <div style={{ marginTop: 32 }}>
                <Link to="/about" className="btn btn-ghost">More about Velo Health <span className="arrow">→</span></Link>
              </div>
            </div>
          </div>
        </div>
      </section>

      {/* Latest journal */}
      <section className="section" style={{ background: 'var(--paper)' }}>
        <div className="wrap">
          <SectionHead eyebrow="From the journal" title="Short pieces. Long rides." link="/journal" linkLabel="All entries" />
          <div className="post-list">
            {posts.map(p => {
              const { meta } = parseMD(p.md);
              return (
                <Link key={p.id} to={'/journal/' + p.id} className="post-row">
                  <span className="pr-date">{meta.date}</span>
                  <span className="pr-title">{meta.title}</span>
                  <span className="pr-cat">{meta.category}</span>
                  <span className="pr-arrow">→</span>
                </Link>
              );
            })}
          </div>
        </div>
      </section>

      {/* Instagram */}
      <section className="ig-strip">
        <div className="wrap">
          <div className="ig-head">
            <div>
              <div className="eyebrow" style={{ marginBottom: 8 }}>From the feed</div>
              <h3>@thegravelloop</h3>
            </div>
            <a href="https://www.instagram.com/thegravelloop/" target="_blank" rel="noreferrer" className="btn btn-ghost">Follow →</a>
          </div>
          <div className="ig-grid">
            {igTiles.map(t => (
              <a key={t.id} href={t.permalink} target="_blank" rel="noreferrer" className="ig-tile" title={t.caption || ''} style={{ backgroundImage: `url(${t.image})` }} />
            ))}
          </div>
        </div>
      </section>
    </>
  );
}

function PodcastPage() {
  const eps = window.VELO_CONTENT.episodes;
  return (
    <>
      <section className="section">
        <div className="wrap-narrow" style={{ paddingTop: 48, paddingBottom: 48 }}>
          <div className="eyebrow" style={{ marginBottom: 20 }}>The Velo Health Podcast</div>
          <h1 className="display" style={{ fontSize: 'clamp(48px, 7vw, 96px)', marginBottom: 24 }}>Long rides.<br/>Long conversations.</h1>
          <p style={{ fontSize: 20, color: 'var(--ink-soft)', maxWidth: '52ch' }}>
            Long-form conversations with gravel athletes about their journey, their gear selection, and the topics around the sport that make gravel worth caring about. A new episode every one to two months, 45–75 minutes, no filler.
          </p>
          <div style={{ display: 'flex', gap: 12, marginTop: 32, flexWrap: 'wrap' }}>
            <a href={SOCIAL.apple} target="_blank" rel="noreferrer" className="btn btn-primary">Apple Podcasts →</a>
            <a href={SOCIAL.spotify} target="_blank" rel="noreferrer" className="btn btn-ghost">Spotify →</a>
            <a href={SOCIAL.youtube} target="_blank" rel="noreferrer" className="btn btn-ghost">YouTube →</a>
            <a href={SOCIAL.rss} target="_blank" rel="noreferrer" className="btn btn-ghost">RSS →</a>
          </div>
        </div>
      </section>
      <section className="section" style={{ background: 'var(--paper)' }}>
        <div className="wrap">
          <SectionHead eyebrow="All episodes" title="The archive, from the top." meta={`${eps.length} episodes`} />
          {eps.map(ep => (
            <Link key={ep.id} to={'/podcast/' + ep.id} className="ep-list-row">
              <span className="n">EP · {ep.number}</span>
              <div className="art-thumb" style={{ backgroundImage: `url(${ep.hero})` }}>
                <span style={{ position: 'relative', background: 'rgba(0,0,0,0.4)', width: '100%', height: '100%', display: 'grid', placeItems: 'center', color: '#faf6ee' }}>{ep.number}</span>
              </div>
              <div className="title-block">
                <h4>{ep.title}</h4>
                <p>With {ep.guest} · {ep.date}</p>
              </div>
              <span className="duration">{ep.duration}</span>
              <span className="play">▶</span>
            </Link>
          ))}
        </div>
      </section>
    </>
  );
}

function EpisodePage({ id }) {
  const ep = window.VELO_CONTENT.episodes.find(e => e.id === id);
  if (!ep) return <NotFound />;
  const { meta, body } = parseMD(ep.md);
  return (
    <>
      <section className="wrap-narrow article-hero">
        <div className="eyebrow">Episode {meta.number} · With {meta.guest} · {meta.duration}</div>
        <h1>{meta.title}</h1>
        <div className="meta">{meta.date}</div>
      </section>
      <div className="wrap-narrow">
        <div className="article-img" style={{ backgroundImage: `url(${meta.hero})` }} />
        <div style={{ padding: '24px 0 32px', borderTop: '1px solid var(--rule)', borderBottom: '1px solid var(--rule)', display: 'flex', gap: 16, alignItems: 'center', flexWrap: 'wrap' }}>
          <a href={meta.youtube || SOCIAL.youtube} target="_blank" rel="noreferrer" className="btn btn-primary">▶ Watch on YouTube</a>
          <a href={meta.apple || SOCIAL.apple} target="_blank" rel="noreferrer" className="btn btn-ghost">Apple →</a>
          <a href={meta.spotify || SOCIAL.spotify} target="_blank" rel="noreferrer" className="btn btn-ghost">Spotify →</a>
        </div>
        <div className="article-body" dangerouslySetInnerHTML={{ __html: renderMD(body) }} />
      </div>
      <NewsletterBlock />
    </>
  );
}

function JournalPage() {
  const posts = window.VELO_CONTENT.posts;
  return (
    <>
      <section className="section">
        <div className="wrap-narrow" style={{ paddingTop: 48, paddingBottom: 48 }}>
          <div className="eyebrow" style={{ marginBottom: 20 }}>The Velo Health Journal</div>
          <h1 className="display" style={{ fontSize: 'clamp(48px, 7vw, 96px)', marginBottom: 24 }}>Short pieces.<br/>Long rides.</h1>
          <p style={{ fontSize: 20, color: 'var(--ink-soft)', maxWidth: '52ch' }}>
            Notes from the saddle, gear tests, and the occasional long essay. Written slowly, read at your pace.
          </p>
        </div>
      </section>
      <section className="section" style={{ background: 'var(--paper)' }}>
        <div className="wrap">
          <div className="post-list">
            {posts.map(p => {
              const { meta } = parseMD(p.md);
              return (
                <Link key={p.id} to={'/journal/' + p.id} className="post-row">
                  <span className="pr-date">{meta.date}</span>
                  <span className="pr-title">{meta.title}</span>
                  <span className="pr-cat">{meta.category}</span>
                  <span className="pr-arrow">→</span>
                </Link>
              );
            })}
          </div>
        </div>
      </section>
    </>
  );
}

function PostPage({ id }) {
  const p = window.VELO_CONTENT.posts.find(p => p.id === id);
  if (!p) return <NotFound />;
  const { meta, body } = parseMD(p.md);
  return (
    <>
      <section className="wrap-narrow article-hero">
        <div className="eyebrow">{meta.category}</div>
        <h1>{meta.title}</h1>
        <div className="meta">{meta.date}</div>
      </section>
      <div className="wrap-narrow">
        <div className="article-img" style={{ backgroundImage: `url(${meta.hero})` }} />
        <div className="article-body" dangerouslySetInnerHTML={{ __html: renderMD(body) }} />
      </div>
      <NewsletterBlock />
    </>
  );
}

function GearPage() {
  const gear = window.VELO_CONTENT.gear;
  return (
    <>
      <section className="section">
        <div className="wrap-narrow" style={{ paddingTop: 48, paddingBottom: 48 }}>
          <div className="eyebrow" style={{ marginBottom: 20 }}>Gear reviews</div>
          <h1 className="display" style={{ fontSize: 'clamp(48px, 7vw, 96px)', marginBottom: 24 }}>Honest gear.<br/>No agenda.</h1>
          <p style={{ fontSize: 20, color: 'var(--ink-soft)', maxWidth: '52ch' }}>
            We pay for most of it. What is gifted is disclosed. What is bad, we say is bad. Long-term notes, not press-release paraphrase.
          </p>
        </div>
      </section>
      <section className="section" style={{ background: 'var(--paper)' }}>
        <div className="wrap">
          <div className="gear-grid">
            {gear.map(g => (
              <Link className="gear-card" key={g.id} to={'/journal/' + g.id}>
                {g.img
                  ? <div className="gear-img" style={{ backgroundImage: `url(${g.img})` }} />
                  : <div className="gear-img placeholder"><span>product shot · {g.cat.toLowerCase()}</span></div>}
                <div className="gear-meta">
                  <span>{g.cat}</span>
                  <span className="rating">{g.rating}</span>
                </div>
                <h4>{g.title}</h4>
                <div className="gear-excerpt">{g.excerpt}</div>
              </Link>
            ))}
          </div>
        </div>
      </section>
    </>
  );
}

function NewsletterPage() {
  const archive = window.VELO_CONTENT.archive || [];
  const [subCount, setSubCount] = useState(null);
  const [live, setLive] = useState([]); // future issues, live from beehiiv

  useEffect(() => {
    let on = true;
    getSubscriberCount().then(c => { if (on) setSubCount(c); });
    fetch('/api/issues')
      .then(r => (r.ok ? r.json() : null))
      .then(d => { if (on && d && Array.isArray(d.items)) setLive(d.items); })
      .catch(() => {});
    return () => { on = false; };
  }, []);

  // Merge live beehiiv issues (external links) with the static back-catalogue
  // (on-site pages). Dedupe by title, preferring the on-site page.
  const norm = s => (s || '').toLowerCase().replace(/\s+/g, ' ').trim();
  const localRows = archive.map(a => ({
    key: a.id, n: a.number, date: a.date, title: a.title,
    excerpt: a.deck, to: '/newsletter/' + a.id, external: false
  }));
  const seen = new Set(localRows.map(r => norm(r.title)));
  const liveRows = live
    .filter(i => !seen.has(norm(i.title)))
    .map(i => ({
      key: 'b:' + (i.url || i.title), n: null, date: i.date, title: i.title,
      excerpt: i.deck, href: i.url, external: true
    }));
  const rows = [...liveRows, ...localRows].sort((a, b) =>
    String(b.date || '').localeCompare(String(a.date || '')));
  const latest = rows[0];

  // Durable, automatic issue numbering. The newest issue renders as
  // "Latest"; every older issue gets a sequential number. Real issue
  // numbers live in the archive frontmatter, so we anchor to the highest
  // known number and count by position. Live beehiiv issues carry no
  // number of their own, but they sit above the archive and get the next
  // number(s) up automatically. As new issues land, everything shifts on
  // its own with nothing to hand-edit.
  const toNum = v => {
    const d = String(v == null ? '' : v).replace(/\D/g, '');
    return d ? parseInt(d, 10) : null;
  };
  let anchorIdx = -1, anchorNum = null;
  for (let i = 0; i < rows.length; i++) {
    const nn = toNum(rows[i].n);
    if (nn != null) { anchorIdx = i; anchorNum = nn; break; }
  }
  const numbered = rows.map((r, i) => ({
    ...r,
    num: anchorNum != null ? anchorNum + (anchorIdx - i) : rows.length - i
  }));

  return (
    <>
      <section className="section">
        <div className="wrap-narrow" style={{ paddingTop: 48, paddingBottom: 48 }}>
          <div className="eyebrow" style={{ marginBottom: 20 }}>The Gravel Loop · A Velo Health newsletter</div>
          <h1 className="display" style={{ fontSize: 'clamp(48px, 7vw, 96px)', marginBottom: 24 }}>The gear, in a loop.</h1>
          <p style={{ fontSize: 20, color: 'var(--ink-soft)', maxWidth: '52ch' }}>
            A product-reviews newsletter for gravel riders who care about what they ride. Honest long-term notes on bikes, tires, kit, tools, and software. Every other Friday.
          </p>
          <div style={{ marginTop: 24, fontSize: 13, color: 'var(--muted)', fontFamily: "'JetBrains Mono', monospace", letterSpacing: '0.06em' }}>
            gravelloop.bike{subCount != null ? ` · ${subCount.toLocaleString()} subscribers` : ''} · every other Friday
          </div>
          {latest && (() => {
            const inner = (
              <>
                <div className="latest-top">
                  <span className="latest-tag">Latest issue</span>
                  <span className="latest-date">{latest.date}</span>
                </div>
                <h3 className="latest-title">{latest.title}</h3>
                {latest.excerpt && <p className="latest-deck">{latest.excerpt}</p>}
                <span className="latest-cta">{latest.external ? 'Read on beehiiv ↗' : 'Read the issue →'}</span>
              </>
            );
            return latest.external ? (
              <a className="latest-card" href={latest.href} target="_blank" rel="noreferrer">{inner}</a>
            ) : (
              <Link className="latest-card" to={latest.to}>{inner}</Link>
            );
          })()}
        </div>
      </section>
      <NewsletterBlock />
      <section className="section">
        <div className="wrap">
          <SectionHead eyebrow="The archive" title="Every issue, from the top." meta={`${rows.length} issues`} />
          <div>
            {numbered.map((r, i) => {
              const inner = (
                <>
                  <span className="iss-n">{i === 0 ? 'Latest' : '№ ' + String(r.num).padStart(3, '0')}</span>
                  <span className="iss-date">{r.date}</span>
                  <div>
                    <h4>{r.title}</h4>
                    <div className="iss-excerpt">{r.excerpt}</div>
                  </div>
                  <span style={{ textAlign: 'right', color: 'var(--muted)' }}>
                    {r.external ? 'Read on beehiiv ↗' : 'Read →'}
                  </span>
                </>
              );
              return r.external ? (
                <a key={r.key} href={r.href} target="_blank" rel="noreferrer" className="issue-row">{inner}</a>
              ) : (
                <Link key={r.key} to={r.to} className="issue-row">{inner}</Link>
              );
            })}
          </div>
        </div>
      </section>
    </>
  );
}

function IssuePage({ id }) {
  const issue = (window.VELO_CONTENT.archive || []).find(a => a.id === id);
  if (!issue) return <NotFound />;
  const { meta, body } = parseMD(issue.md);
  return (
    <>
      <section className="wrap-narrow article-hero">
        <div className="eyebrow">The Gravel Loop · № {meta.number} · {meta.date}</div>
        <h1>{meta.title}</h1>
        {meta.deck && <div className="meta">{meta.deck}</div>}
      </section>
      <div className="wrap-narrow">
        <div className="article-body" dangerouslySetInnerHTML={{ __html: renderMD(body.replace(/^#\s+.*\n+/, '')) }} />
      </div>
      <NewsletterBlock />
    </>
  );
}

function AboutPage() {
  return (
    <>
      <section className="section">
        <div className="wrap-narrow" style={{ paddingTop: 48, paddingBottom: 48 }}>
          <div className="eyebrow" style={{ marginBottom: 20 }}>About Velo Health</div>
          <h1 className="display" style={{ fontSize: 'clamp(48px, 7vw, 96px)', marginBottom: 24 }}>An independent gravel cycling media brand.</h1>
          <p style={{ fontSize: 22, color: 'var(--ink-soft)', maxWidth: '52ch' }}>
            We ride, we talk to the riders, we test the gear, we tell the truth about it. No parent company. No conflicts. No claims we can't defend.
          </p>
        </div>
      </section>
      <section className="section" style={{ background: 'var(--paper)' }}>
        <div className="wrap">
          <div className="about-block">
            <div className="about-img" style={{ backgroundImage: 'url(assets/cyclist.jpg)' }} />
            <div>
              <div className="eyebrow" style={{ marginBottom: 16 }}>Who rides this</div>
              <h2>Mike Khosravani, founder.</h2>
              <p>
                Mike is a Toronto-based gravel rider with a serious problem: he can't stop. It started a few years ago, after a long stretch of road riding, a longer stretch off the bike entirely, and a slightly desperate hunt for the one thing that would drag him back outside. Gravel won. It always wins.
              </p>
              <p>
                He started Velo Health to make the kind of stuff he was always looking for and could never find — honest stories, gear reviews that actually tell you something, the long conversations that happen on long rides. By day he's a physician and neuroscientist, which mostly means he's incapable of testing a tire without running a controlled experiment in his head and is unreasonably curious about why your legs feel the way they do at hour four. That brain shapes how he tests gear and thinks about riding. It is not what this is about. Velo Health and The Gravel Loop are pure gravel.
              </p>
              <div className="sig">— Mike</div>
            </div>
          </div>
        </div>
      </section>
      <section className="section">
        <div className="wrap">
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 48 }}>
            {[
              { n: '01', t: 'Rider voices', b: 'Long-format interviews with racers, mechanics, engineers, event directors, and characters in the sport.' },
              { n: '02', t: 'Honest gear', b: 'Reviews, comparisons, "What I\'m Riding" series. Unsponsored opinions. Real miles. Disclosed gifts.' },
              { n: '03', t: 'The gravel life', b: 'Culture, travel, food, events, community, training philosophy — in general terms, never prescriptive.' }
            ].map(p => (
              <div key={p.n}>
                <div className="eyebrow" style={{ marginBottom: 16 }}>{p.n} · Pillar</div>
                <h3 style={{ fontSize: 26, marginBottom: 12 }}>{p.t}</h3>
                <p style={{ color: 'var(--ink-soft)' }}>{p.b}</p>
              </div>
            ))}
          </div>
        </div>
      </section>
      <NewsletterBlock />
    </>
  );
}

function EventsPage() {
  const events = window.VELO_CONTENT.events;
  return (
    <>
      <section className="section">
        <div className="wrap-narrow" style={{ paddingTop: 48, paddingBottom: 48 }}>
          <div className="eyebrow" style={{ marginBottom: 20 }}>Races</div>
          <h1 className="display" style={{ fontSize: 'clamp(48px, 7vw, 96px)', marginBottom: 24 }}>Show up.<br/>Ride long.</h1>
          <p style={{ fontSize: 20, color: 'var(--ink-soft)', maxWidth: '52ch' }}>
            Races are where you find out what you are made of. The clock does not care about excuses, and the start line does not care how the training went. You sign up, you show up, and you ride until you cross that finish line. That is the whole point. Everything you learn about yourself out there comes from putting yourself in the arena and seeing it through.
          </p>
          <ul style={{ fontSize: 18, color: 'var(--ink-soft)', maxWidth: '52ch', marginTop: 24, lineHeight: 1.7, paddingLeft: '1.2em' }}>
            <li>Use races to inspire your training.</li>
            <li>Use races to test your gear.</li>
            <li>Use races to test yourself effectively.</li>
          </ul>
        </div>
      </section>
      <section className="section" style={{ background: 'var(--paper)' }}>
        <div className="wrap">
          <SectionHead eyebrow="Race results" title="Races/Events ridden." meta={`${events.length} races/events`} />
          {events.map(e => (
            <div key={e.id} className="event-card">
              <div className="event-date">{e.day}<span className="mo">{e.mo} · {e.year}</span></div>
              <div>
                <h3>{e.title}{e.done && <span className="event-tag">Done</span>}</h3>
                <div className="locn">{e.km} km{e.elev ? ` · ${e.elev} m ↑` : ''}</div>
              </div>
            </div>
          ))}
          {/* Strava club widget — temporarily disabled, was not loading
          <div style={{ marginTop: 48 }}>
            <SectionHead eyebrow="On Strava" title="Latest club rides." />
            <iframe
              allowTransparency="true"
              frameBorder="0"
              height="160"
              scrolling="no"
              src="https://www.strava.com/clubs/2178359/latest-rides/0cc99f77910266b8d1fa488c5d017039f8e4062f?show_rides=false"
              width="300"
              title="The Gravel Loop on Strava"
            ></iframe>
          </div>
          */}
        </div>
      </section>
    </>
  );
}

function MembershipPage() {
  return (
    <>
      <section className="section">
        <div className="wrap-narrow" style={{ paddingTop: 48, paddingBottom: 48 }}>
          <div className="eyebrow" style={{ marginBottom: 20 }}>Members</div>
          <h1 className="display" style={{ fontSize: 'clamp(48px, 7vw, 96px)', marginBottom: 24 }}>Support the work.<br/>Ride with us.</h1>
          <p style={{ fontSize: 20, color: 'var(--ink-soft)', maxWidth: '52ch' }}>
            A small paid tier that helps us keep the lights on, the ads off, and the conversations honest. Includes a private Discord, ad-free feeds, partner discounts, and first dibs on retreats.
          </p>
        </div>
      </section>
      <section className="section" style={{ background: 'var(--paper)' }}>
        <div className="wrap">
          <div style={{ marginBottom: 64, padding: 32, border: '1px solid var(--rule)', background: 'var(--paper)' }}>
            <div className="eyebrow" style={{ marginBottom: 16 }}>Custom program · gear &amp; technology mentorship</div>
            <h3 style={{ fontSize: 'clamp(28px, 4vw, 44px)', marginTop: 0, marginBottom: 20 }}>Get the right gear. Use the right tools. Skip the hype.</h3>
            <p style={{ fontSize: 17, color: 'var(--ink-soft)', marginTop: 0, maxWidth: '72ch' }}>
              The bike industry is loud. There is a lot of choice, a lot of hype, and a lot of money spent on the wrong things. This is one-on-one mentorship for riders who want help cutting through it.
            </p>
            <p style={{ fontSize: 17, color: 'var(--ink-soft)', maxWidth: '72ch' }}>
              We help you choose the right gear for you and the riding you actually do, not the gear an ad wants you to buy, and set it up correctly so it works and you stay safe on it. From there, we show you how to use the technology already in your hands, power meters, Strava, and the major training platforms, so you can read your own metrics and guide your own training with confidence.
            </p>
            <p style={{ fontSize: 17, color: 'var(--ink-soft)', maxWidth: '72ch' }}>
              This is mentorship on the gear and the tech, nothing more. The training stays self-guided and yours. It is not a training plan written for you, and it is not medical, nutritional, or rehab advice. It is one experienced rider helping another spend wisely, set up safely, and use modern tools well.
            </p>
            <Link to="/contact" className="btn btn-primary" style={{ marginTop: 8 }}>Get in touch <span className="arrow">→</span></Link>
          </div>
          <div className="plan-grid">
            <div className="plan">
              <div className="eyebrow">Monthly</div>
              <h3>Rider</h3>
              <div className="plan-price">$5<sub> / month</sub></div>
              <ul>
                <li>Ad-free podcast &amp; video feed</li>
                <li>Members-only Discord</li>
                <li>Partner discounts (Rapha, MAAP, TrainerRoad)</li>
                <li>Early access to retreats</li>
                <li>Monthly gear desk drop</li>
              </ul>
              <stripe-buy-button
                buy-button-id="buy_btn_1TYfFSLHkGsHxAf1ciV8EGRs"
                publishable-key="pk_live_51ILJMpLHkGsHxAf1MmMXyMQxjkkN55FH8Ca9zxM4SMZrC6rzzFMwQT0fYMTci0Pr57F75TxQqYSdv8GTy4ORh6or00OjFqMVtA"
              ></stripe-buy-button>
            </div>
            <div className="plan featured">
              <div className="eyebrow" style={{ color: 'var(--accent-soft)' }}>Annual · two months free</div>
              <h3>Rider, annual</h3>
              <div className="plan-price">$50<sub> / year</sub></div>
              <ul>
                <li>Everything in monthly</li>
                <li>Members-only written pieces</li>
                <li>Annual Velo Health cap</li>
                <li>Early heads-up on group rides</li>
                <li>Founding member listing</li>
              </ul>
              <stripe-buy-button
                buy-button-id="buy_btn_1TYfKbLHkGsHxAf1Hmd0okVS"
                publishable-key="pk_live_51ILJMpLHkGsHxAf1MmMXyMQxjkkN55FH8Ca9zxM4SMZrC6rzzFMwQT0fYMTci0Pr57F75TxQqYSdv8GTy4ORh6or00OjFqMVtA"
              ></stripe-buy-button>
            </div>
          </div>
          <div style={{ marginTop: 32, padding: 24, border: '1px solid var(--rule)', background: 'var(--paper)', fontSize: 14, color: 'var(--ink-soft)' }}>
            <div className="eyebrow" style={{ marginBottom: 12 }}>Disclosure</div>
            <p style={{ margin: 0, maxWidth: '72ch', lineHeight: 1.6 }}>
              Signing up gets you exactly what is listed above, and nothing more. Your membership, and any gear and technology mentorship, is general, educational, and for entertainment. It does not constitute medical, clinical, rehabilitative, diagnostic, or personalized training advice, and it is not a substitute for consultation with a qualified physician, physiotherapist, registered dietitian, or certified coach.
            </p>
            <p style={{ margin: '12px 0 0', maxWidth: '72ch', lineHeight: 1.6 }}>
              Becoming a member does not create a physician-patient relationship, a duty of care, or any medical or therapeutic relationship with Velo Health Ltd. or any of its team, regardless of whether any team member is separately licensed in another field. Velo Health Ltd. is an Ontario corporation and is not a medical clinic, a medicine professional corporation, or a licensed healthcare facility. If you have a medical concern, an injury, or a specific health condition, please consult your own healthcare providers before acting on any general information you find through Velo Health.
            </p>
          </div>
          <div style={{ marginTop: 32, padding: 32, border: '1px solid var(--rule)', background: 'var(--paper)' }}>
            <div className="eyebrow" style={{ marginBottom: 16 }}>What members get, and don't</div>
            <p style={{ fontSize: 17, color: 'var(--ink-soft)', margin: 0, maxWidth: '72ch' }}>
              Membership is a fan club, and the mentorship is gear and technology guidance. Neither is a clinic. You do not get a training plan written for you, or medical, nutritional, or rehab advice, and your training always stays self-guided. Anything to do with your health belongs with professionals licensed and insured to provide it. What you do get: community, bonus content, honest gear talk, help choosing and setting up the right equipment, and guidance on using the technology already available to you.
            </p>
          </div>
        </div>
      </section>
    </>
  );
}

function ContactPage() {
  const [form, setForm] = useState({ name: '', email: '', subject: 'General', message: '', company: '' });
  const [status, setStatus] = useState('idle'); // idle | sending | sent | error
  const set = k => e => setForm(f => ({ ...f, [k]: e.target.value }));

  const mailto = () => {
    const body = `Name: ${form.name}\nEmail: ${form.email}\nSubject: ${form.subject}\n\n${form.message}`;
    return `mailto:velohealthltd@gmail.com?subject=${encodeURIComponent('[Contact] ' + form.subject + ' — ' + form.name)}&body=${encodeURIComponent(body)}`;
  };

  const submit = async (e) => {
    e.preventDefault();
    if (status === 'sending') return;
    setStatus('sending');
    try {
      const r = await fetch('/api/contact', {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify(form)
      });
      const d = await r.json().catch(() => ({}));
      setStatus(r.ok && d.ok ? 'sent' : 'error');
    } catch {
      setStatus('error');
    }
  };

  return (
    <section className="section">
      <div className="wrap-narrow" style={{ paddingTop: 48 }}>
        <div className="eyebrow" style={{ marginBottom: 20 }}>Get in touch</div>
        <h1 className="display" style={{ fontSize: 'clamp(48px, 7vw, 96px)', marginBottom: 24 }}>Say hello.</h1>
        <p style={{ fontSize: 20, color: 'var(--ink-soft)', maxWidth: '52ch' }}>
          Guest pitches, brand partnerships, consulting, feedback on a review, a good book recommendation. All welcome. We answer most within a week.
        </p>
        {status === 'sent' ? (
          <div style={{ marginTop: 48, padding: 48, border: '1px solid var(--rule)', background: 'var(--paper)' }}>
            <div className="eyebrow" style={{ marginBottom: 8 }}>Received</div>
            <h3 style={{ fontSize: 28 }}>Thanks — we'll be in touch.</h3>
          </div>
        ) : (
          <form className="contact-form" onSubmit={submit}>
            <div><label>Name</label><input type="text" required value={form.name} onChange={set('name')} /></div>
            <div><label>Email</label><input type="email" required value={form.email} onChange={set('email')} /></div>
            <div>
              <label>Subject</label>
              <select value={form.subject} onChange={set('subject')}>
                <option>Guest pitch for the podcast</option>
                <option>Brand partnership / sponsorship</option>
                <option>B2B consulting</option>
                <option>Gear for review</option>
                <option>General</option>
              </select>
            </div>
            <div><label>Message</label><textarea required value={form.message} onChange={set('message')} /></div>
            {/* Honeypot — hidden from real users */}
            <input type="text" name="company" tabIndex="-1" autoComplete="off" value={form.company} onChange={set('company')} style={{ position: 'absolute', left: '-9999px', width: 1, height: 1 }} aria-hidden="true" />
            <button type="submit" className="btn btn-primary" style={{ justifySelf: 'start' }} disabled={status === 'sending'}>
              {status === 'sending' ? 'Sending…' : 'Send →'}
            </button>
            {status === 'error' && (
              <div style={{ color: 'var(--accent, #c0392b)', fontSize: 14 }}>
                Couldn’t send through the site. <a href={mailto()}>Email velohealthltd@gmail.com directly →</a>
              </div>
            )}
          </form>
        )}
      </div>
    </section>
  );
}

function NotFound() {
  return (
    <section className="section">
      <div className="wrap-narrow" style={{ paddingTop: 120, paddingBottom: 120 }}>
        <div className="eyebrow">404 · Off the route</div>
        <h1 className="display" style={{ fontSize: 'clamp(48px, 7vw, 96px)', margin: '20px 0 24px' }}>This road doesn't go anywhere.</h1>
        <Link to="/" className="btn btn-primary">Back to home →</Link>
      </div>
    </section>
  );
}

Object.assign(window, {
  HomePage, PodcastPage, EpisodePage, JournalPage, PostPage,
  GearPage, NewsletterPage, IssuePage, AboutPage, EventsPage, MembershipPage,
  ContactPage, NotFound
});
