/* Guide (#/guide) — onboarding for new users and members: what SignalShade does,
 * who sees what, connecting an exchange, following a strategy, the settings that size and
 * protect each trade, and how to read a strategy page.
 *
 * Public on purpose (no roles): pending users and signed-out visitors are exactly who it's
 * for. Every label quoted here is the app's own wording (My account, strategy pages), so a
 * reader can find the thing on screen. Screenshots live in public/guide/ and are captured
 * from the real pages against fixture data (scripts/capture-guide-screenshots.js).
 *
 * Motion: one orchestrated hero (a trader's message becoming a protected order) and scroll
 * reveals per chapter. All of it stops under prefers-reduced-motion. Styles are scoped to
 * .gd-* classes in a <style> block below, using the app's own CSS variables.
 */

const { useState: useStateGD, useEffect: useEffectGD, useRef: useRefGD } = React;

const GUIDE_CSS = `
.gd { --gd-line: color-mix(in oklch, var(--gold) 35%, transparent); max-width: 1080px; margin: 0 auto; position: relative; overflow-x: clip; }
.gd * { box-sizing: border-box; }
.gd-grain { position: absolute; inset: 0; pointer-events: none; opacity: .5; z-index: 0;
  background-image: radial-gradient(color-mix(in oklch, var(--text) 5%, transparent) 1px, transparent 1px);
  background-size: 3px 3px; mask-image: radial-gradient(ellipse at 50% 0%, #000 0%, transparent 70%); }
.gd > section { position: relative; z-index: 1; }

/* hero */
.gd-hero { display: grid; grid-template-columns: 1.05fr 1fr; gap: 34px; align-items: center; padding: 18px 0 44px; }
.gd-kicker { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .22em; text-transform: uppercase; color: var(--gold); }
.gd-title { font-family: var(--font-serif); font-style: italic; font-weight: 400; font-size: clamp(34px, 4.6vw, 54px);
  line-height: 1.02; letter-spacing: -.02em; margin: 14px 0 16px; }
.gd-title em { font-style: normal; font-family: var(--font-sans); font-weight: 600; color: var(--gold); letter-spacing: -.03em; }
.gd-lede { color: var(--text-2); font-size: 15px; line-height: 1.6; max-width: 46ch; }
.gd-ctas { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 22px; }
.gd-btn { padding: 9px 16px; border-radius: 8px; font: 600 12.5px var(--font-sans); cursor: pointer; border: 1px solid var(--border);
  background: var(--bg-card); color: var(--text); transition: transform .15s ease, border-color .15s ease, background .15s ease; }
.gd-btn:hover { transform: translateY(-1px); border-color: var(--border-strong); }
.gd-btn.primary { background: var(--gold); color: var(--bg-base); border-color: var(--gold); }

/* the pipeline animation */
.gd-pipe { position: relative; background: linear-gradient(160deg, var(--bg-card), var(--bg-app)); border: 1px solid var(--border-soft);
  border-radius: var(--r-lg); padding: 18px; box-shadow: 0 30px 60px -30px oklch(0 0 0 / .7); overflow: hidden; }
.gd-pipe::before { content: ""; position: absolute; inset: 0; background: radial-gradient(500px 200px at 80% -10%, var(--gold-bg), transparent 70%); pointer-events: none; }
.gd-stage { position: relative; border: 1px solid var(--border-soft); border-radius: 10px; padding: 12px 13px; background: var(--bg-base);
  opacity: .35; transform: translateY(6px); transition: opacity .5s ease, transform .5s ease, border-color .5s ease; }

.gd-stage.on { opacity: 1; transform: none; border-color: var(--gd-line); }
.gd-stage-lbl { font: 500 9.5px var(--font-mono); letter-spacing: .14em; text-transform: uppercase; color: var(--text-4); margin-bottom: 7px; display: flex; justify-content: space-between; }
.gd-stage.on .gd-stage-lbl { color: var(--gold); }
.gd-wire { position: relative; width: 1px; height: 26px; margin: 0 auto; background: var(--border-soft); overflow: hidden; }
.gd-wire::after { content: ""; position: absolute; left: -1px; width: 3px; height: 9px; border-radius: 2px; background: var(--gold); top: -10px; opacity: 0; }
.gd-wire.on::after { animation: gdPulse .7s ease-in forwards; }
@keyframes gdPulse { 0% { top: -10px; opacity: 1; } 100% { top: 26px; opacity: 0; } }
.gd-msg { font-size: 12.5px; line-height: 1.5; color: var(--text-2); }
.gd-msg b { color: var(--text); font-weight: 600; }
.gd-hl { border-radius: 3px; padding: 0 2px; transition: background .4s ease, color .4s ease; }
.gd-hl.on.e { background: var(--gold-bg); color: var(--gold); }
.gd-hl.on.s { background: var(--bear-bg); color: var(--bear); }
.gd-hl.on.t { background: var(--bull-bg); color: var(--bull); }
.gd-fields { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }
.gd-field { background: var(--bg-card); border-radius: 6px; padding: 6px 8px; }
.gd-field i { display: block; font: normal 9px var(--font-mono); color: var(--text-4); letter-spacing: .1em; text-transform: uppercase; }
.gd-field span { font: 600 13px var(--font-mono); }
.gd-checks { display: flex; flex-wrap: wrap; gap: 5px; }
.gd-check { font: 500 10.5px var(--font-mono); padding: 3px 8px; border-radius: 999px; border: 1px solid var(--border-soft); color: var(--text-4);
  transition: color .3s ease, border-color .3s ease, background .3s ease; }
.gd-check.on { color: var(--bull); border-color: color-mix(in oklch, var(--bull) 45%, transparent); background: var(--bull-bg); }
.gd-order { display: flex; justify-content: space-between; align-items: center; gap: 10px; font-family: var(--font-mono); font-size: 12px; }
.gd-order .px { font-size: 18px; font-weight: 600; }
.gd-replay { display: block; margin: 10px 0 -4px auto; font: 500 10px var(--font-mono); color: var(--text-4); background: none; border: none; cursor: pointer; letter-spacing: .1em; position: relative; z-index: 2; }
.gd-replay:hover { color: var(--gold); }

/* chapters */
.gd-toc { position: sticky; top: var(--gd-top, 0px); z-index: 3; display: flex; gap: 4px; overflow-x: auto; max-width: 100%; padding: 10px 0; margin-bottom: 8px;
  background: linear-gradient(var(--bg-app) 70%, transparent); scrollbar-width: none; }
.gd-toc button { flex: 0 0 auto; font: 500 11px var(--font-sans); color: var(--text-3); background: none; border: 1px solid transparent; border-radius: 999px; padding: 5px 11px; cursor: pointer; }
.gd-toc button:hover { color: var(--text); }
.gd-toc button.on { color: var(--gold); border-color: var(--gd-line); background: var(--gold-bg); }
.gd-ch { display: grid; grid-template-columns: 120px 1fr; gap: 28px; padding: 46px 0; border-top: 1px solid var(--border-soft); }
.gd-num { font: 300 64px/0.9 var(--font-mono); color: transparent; -webkit-text-stroke: 1px var(--gd-line); letter-spacing: -.04em; position: sticky; top: calc(var(--gd-top, 0px) + 64px); align-self: start; }
.gd-ch { scroll-margin-top: calc(var(--gd-top, 0px) + 54px); }
.gd-h2 { font-family: var(--font-serif); font-style: italic; font-weight: 400; font-size: 30px; letter-spacing: -.01em; margin: 0 0 6px; }
.gd-sub { color: var(--text-3); font-size: 14px; line-height: 1.6; margin: 0 0 22px; max-width: 64ch; }
.gd-p { color: var(--text-2); font-size: 13.5px; line-height: 1.65; max-width: 68ch; }
.gd-p b { color: var(--text); font-weight: 600; }
.gd-q { font-family: var(--font-mono); font-size: 12px; color: var(--gold); background: var(--gold-bg); padding: 1px 6px; border-radius: 4px; white-space: nowrap; }

.gd-reveal { opacity: 0; transform: translateY(18px); transition: opacity .7s cubic-bezier(.2,.7,.2,1), transform .7s cubic-bezier(.2,.7,.2,1); }
.gd-reveal.in { opacity: 1; transform: none; }
.gd-reveal.d1 { transition-delay: .08s; } .gd-reveal.d2 { transition-delay: .16s; } .gd-reveal.d3 { transition-delay: .24s; } .gd-reveal.d4 { transition-delay: .32s; }

.gd-grid { display: grid; gap: 12px; }
.gd-g2 { grid-template-columns: repeat(2, 1fr); } .gd-g3 { grid-template-columns: repeat(3, 1fr); } .gd-g4 { grid-template-columns: repeat(4, 1fr); }
.gd-tile { background: var(--bg-card); border: 1px solid var(--border-soft); border-radius: var(--r); padding: 15px 16px; position: relative; overflow: hidden;
  transition: border-color .2s ease, transform .2s ease; }
.gd-tile:hover { border-color: var(--border); transform: translateY(-2px); }
.gd-tile h4 { margin: 0 0 6px; font-size: 13.5px; font-weight: 600; display: flex; align-items: center; gap: 8px; }
.gd-tile p { margin: 0; color: var(--text-3); font-size: 12.5px; line-height: 1.55; }
.gd-tile .tag { font: 500 9.5px var(--font-mono); letter-spacing: .12em; text-transform: uppercase; color: var(--text-4); margin-bottom: 8px; display: block; }
.gd-tile.accent::after { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px; background: var(--gold); }

/* steps */
.gd-steps { counter-reset: s; display: flex; flex-direction: column; gap: 0; position: relative; }
.gd-step { display: grid; grid-template-columns: 34px 1fr; gap: 14px; padding: 0 0 22px; position: relative; }
.gd-step::before { counter-increment: s; content: counter(s); width: 28px; height: 28px; border-radius: 50%; display: grid; place-items: center;
  font: 600 12px var(--font-mono); color: var(--bg-base); background: var(--gold); position: relative; z-index: 1; }
.gd-step::after { content: ""; position: absolute; left: 13.5px; top: 30px; bottom: 2px; width: 1px; background: var(--gd-line); }
.gd-step:last-child::after { display: none; }
.gd-step h5 { margin: 3px 0 4px; font-size: 13.5px; font-weight: 600; }
.gd-step p { margin: 0; color: var(--text-3); font-size: 12.5px; line-height: 1.6; }

/* callouts */
.gd-note { display: flex; gap: 11px; padding: 12px 14px; border-radius: var(--r); border: 1px solid var(--border-soft); background: var(--bg-card); font-size: 12.5px; color: var(--text-2); line-height: 1.55; }
.gd-note.warn { border-color: color-mix(in oklch, var(--bear) 40%, transparent); background: var(--bear-bg); }
.gd-note.tip { border-color: var(--gd-line); background: var(--gold-bg); }
.gd-note .ic { font-size: 15px; line-height: 1.2; }

/* screenshots */
.gd-shot { border-radius: var(--r); border: 1px solid var(--border-soft); overflow: hidden; background: var(--bg-base); box-shadow: 0 24px 50px -28px oklch(0 0 0 / .8); }
.gd-shot .gd-chrome { height: 22px; display: flex; align-items: center; gap: 5px; padding: 0 9px; background: var(--bg-card); border-bottom: 1px solid var(--border-soft); }
.gd-shot .gd-chrome i { width: 7px; height: 7px; border-radius: 50%; background: var(--border-strong); }
.gd-shot .gd-chrome span { margin-left: 8px; font: 10px var(--font-mono); color: var(--text-4); }
.gd-shot img { display: block; width: 100%; height: auto; }
.gd-cap { font-size: 11px; color: var(--text-4); margin-top: 7px; }

/* roles */
.gd-roles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0; border: 1px solid var(--border-soft); border-radius: var(--r); overflow: hidden; }
.gd-role { padding: 14px 15px; background: var(--bg-card); border-right: 1px solid var(--border-soft); }
.gd-role:last-child { border-right: none; }
.gd-role.me { background: linear-gradient(180deg, var(--gold-bg), var(--bg-card) 60%); }
.gd-role h4 { margin: 0 0 2px; font-size: 13.5px; }
.gd-role .who { font: 10px var(--font-mono); color: var(--text-4); letter-spacing: .08em; text-transform: uppercase; }
.gd-role ul { margin: 10px 0 0; padding-left: 15px; color: var(--text-3); font-size: 12px; line-height: 1.6; }
.gd-role .you { font: 600 9px var(--font-mono); color: var(--gold); letter-spacing: .14em; margin-left: 6px; }

/* sizing calculator */
.gd-calc { display: grid; grid-template-columns: 1fr 1.1fr; gap: 16px; align-items: stretch; }
.gd-calc label { display: block; font: 500 9.5px var(--font-mono); letter-spacing: .1em; text-transform: uppercase; color: var(--text-4); margin: 0 0 4px; }
.gd-calc input[type=range] { width: 100%; accent-color: var(--gold); }
.gd-calc .row { margin-bottom: 13px; }
.gd-calc .val { float: right; font: 600 12px var(--font-mono); color: var(--text); text-transform: none; letter-spacing: 0; }
.gd-out { display: flex; flex-direction: column; justify-content: center; gap: 12px; }
.gd-big { font: 600 30px var(--font-mono); letter-spacing: -.02em; }
.gd-bar { height: 8px; border-radius: 4px; background: var(--bg-elev); overflow: hidden; }
.gd-bar > i { display: block; height: 100%; background: var(--gold); transition: width .35s cubic-bezier(.2,.7,.2,1); }
.gd-bar.cap > i { background: var(--info); }

/* glossary */
.gd-gloss { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0 26px; }
.gd-gloss div { padding: 11px 0; border-bottom: 1px solid var(--border-soft); }
.gd-gloss dt { font: 600 12.5px var(--font-mono); color: var(--gold); }
.gd-gloss dd { margin: 3px 0 0; color: var(--text-3); font-size: 12.5px; line-height: 1.55; }

@media (max-width: 860px) {
  .gd-hero, .gd-calc { grid-template-columns: 1fr; }
  .gd-ch { grid-template-columns: 1fr; gap: 6px; padding: 34px 0; }
  .gd-num { position: static; font-size: 44px; }
  .gd-g3, .gd-g4 { grid-template-columns: repeat(2, 1fr); }
  .gd-roles { grid-template-columns: 1fr; }
  .gd-role { border-right: none; border-bottom: 1px solid var(--border-soft); } .gd-role:last-child { border-bottom: none; }
  .gd-gloss, .gd-g2 { grid-template-columns: 1fr; }
}
@media (max-width: 520px) { .gd-g3, .gd-g4, .gd-fields { grid-template-columns: 1fr 1fr; } }
@media (prefers-reduced-motion: reduce) {
  .gd-reveal { opacity: 1; transform: none; transition: none; }
  .gd-stage { opacity: 1; transform: none; transition: none; }
  .gd-wire::after { display: none; }
  .gd-tile:hover, .gd-btn:hover { transform: none; }
}
`;

function useReducedMotion() {
  const [r, setR] = useStateGD(() => window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches);
  useEffectGD(() => {
    if (!window.matchMedia) return undefined;
    const mq = window.matchMedia('(prefers-reduced-motion: reduce)');
    const on = () => setR(mq.matches);
    mq.addEventListener('change', on);
    return () => mq.removeEventListener('change', on);
  }, []);
  return r;
}

// Adds .in to every .gd-reveal inside `root` as it scrolls into view.
function useReveal(rootRef) {
  useEffectGD(() => {
    const root = rootRef.current;
    if (!root) return undefined;
    const els = root.querySelectorAll('.gd-reveal');
    if (!('IntersectionObserver' in window)) { els.forEach(e => e.classList.add('in')); return undefined; }
    const io = new IntersectionObserver(entries => {
      entries.forEach(en => { if (en.isIntersecting) { en.target.classList.add('in'); io.unobserve(en.target); } });
    }, { rootMargin: '0px 0px -8% 0px', threshold: 0.08 });
    els.forEach(e => io.observe(e));
    return () => io.disconnect();
  }, []);
}

// ── hero: a trader's message becoming a protected order ────────────────────
const PIPE_STEPS = 9;

function PipelineHero() {
  const reduced = useReducedMotion();
  const [t, setT] = useStateGD(reduced ? PIPE_STEPS : 0);
  const [run, setRun] = useStateGD(0);
  useEffectGD(() => {
    if (reduced) { setT(PIPE_STEPS); return undefined; }
    setT(0);
    const ids = [];
    for (let i = 1; i <= PIPE_STEPS; i++) ids.push(setTimeout(() => setT(i), 450 + i * 520));
    return () => ids.forEach(clearTimeout);
  }, [run, reduced]);

  const on = n => t >= n;
  return (
    <div className="gd-pipe" aria-label="How a trader's message becomes an order">
      <div className={'gd-stage' + (on(1) ? ' on' : '')}>
        <div className="gd-stage-lbl"><span>1 · Trader posts</span><span>Telegram</span></div>
        <div className="gd-msg">
          <b>$SOL</b> Buying setup · Entry <span className={'gd-hl e' + (on(2) ? ' on' : '')}>142.50</span> ·
          SL <span className={'gd-hl s' + (on(2) ? ' on' : '')}>136.80</span> ·
          TP <span className={'gd-hl t' + (on(2) ? ' on' : '')}>151 / 158</span>
        </div>
      </div>
      <div className={'gd-wire' + (on(3) ? ' on' : '')} />
      <div className={'gd-stage' + (on(3) ? ' on' : '')}>
        <div className="gd-stage-lbl"><span>2 · Read &amp; extracted</span><span>long</span></div>
        <div className="gd-fields">
          {[['coin', 'SOL'], ['entry', '142.50'], ['stop', '136.80'], ['TP1', '151.00']].map(([k, v]) => (
            <div key={k} className="gd-field"><i>{k}</i><span style={{ color: k === 'stop' ? 'var(--bear)' : k === 'TP1' ? 'var(--bull)' : undefined }}>{v}</span></div>
          ))}
        </div>
      </div>
      <div className={'gd-wire' + (on(5) ? ' on' : '')} />
      <div className={'gd-stage' + (on(5) ? ' on' : '')}>
        <div className="gd-stage-lbl"><span>3 · Safety checks</span><span>{on(8) ? 'passed' : 'checking'}</span></div>
        <div className="gd-checks">
          {['has a stop', 'stop not hit', 'price still near entry', 'reward:risk ≥ 1.5', 'within your limits'].map((c, i) => (
            <span key={c} className={'gd-check' + (on(5 + Math.min(3, Math.floor(i / 2) + 1)) ? ' on' : '')}>{on(5 + Math.min(3, Math.floor(i / 2) + 1)) ? '✓ ' : ''}{c}</span>
          ))}
        </div>
      </div>
      <div className={'gd-wire' + (on(9) ? ' on' : '')} />
      <div className={'gd-stage' + (on(9) ? ' on' : '')}>
        <div className="gd-stage-lbl"><span>4 · On your exchange</span><span>{on(9) ? 'protected' : '—'}</span></div>
        <div className="gd-order">
          <span><span className="chip bull" style={{ fontSize: 9.5 }}>long</span> SOL · sized to <b>1R</b></span>
          <span className="px">{on(9) ? '142.50' : '—'}</span>
          <span style={{ color: 'var(--bear)' }}>SL 136.80</span>
        </div>
      </div>
      {!reduced && <button className="gd-replay" onClick={() => setRun(r => r + 1)}>↻ replay</button>}
    </div>
  );
}

// ── pieces ─────────────────────────────────────────────────────────────────
function Shot({ src, alt, path, caption }) {
  const [ok, setOk] = useStateGD(true);
  if (!ok) return null;   // no screenshot shipped yet: the text stands on its own
  return (
    <figure className="gd-reveal d2" style={{ margin: '18px 0 0' }}>
      <div className="gd-shot">
        <div className="gd-chrome"><i /><i /><i /><span>signalshade.com/{path}</span></div>
        <img src={src} alt={alt} loading="lazy" onError={() => setOk(false)} />
      </div>
      {caption && <figcaption className="gd-cap">{caption}</figcaption>}
    </figure>
  );
}

function Chapter({ id, num, title, sub, children }) {
  return (
    <section className="gd-ch" id={'gd-' + id} data-ch={id}>
      <div className="gd-num" aria-hidden="true">{num}</div>
      <div>
        <h2 className="gd-h2 gd-reveal">{title}</h2>
        {sub && <p className="gd-sub gd-reveal d1">{sub}</p>}
        {children}
      </div>
    </section>
  );
}

const Q = ({ children }) => <span className="gd-q">{children}</span>;

// Same maths as the bot (ai/src/sizing.py): size = (equity × risk %) ÷ distance to the stop,
// then capped by the max position %. Shown to the cent so the reader can check it.
function SizingCalc() {
  const [equity, setEquity] = useStateGD(5000);
  const [risk, setRisk] = useStateGD(1);
  const [stopPct, setStopPct] = useStateGD(4);
  const [capPct, setCapPct] = useStateGD(0);
  const riskUsd = equity * risk / 100;
  const rawNotional = riskUsd / (stopPct / 100);
  const capUsd = capPct > 0 ? equity * capPct / 100 : null;
  const notional = capUsd != null ? Math.min(rawNotional, capUsd) : rawNotional;
  const capped = capUsd != null && rawNotional > capUsd;
  const lossAtStop = notional * stopPct / 100;
  const usd = v => '$' + v.toLocaleString(undefined, { maximumFractionDigits: v < 100 ? 2 : 0 });
  const slider = (label, value, set, min, max, step, fmt) => (
    <div className="row">
      <label>{label}<span className="val">{fmt(value)}</span></label>
      <input type="range" min={min} max={max} step={step} value={value} onChange={e => set(Number(e.target.value))} />
    </div>
  );
  return (
    <div className="gd-calc gd-reveal d2">
      <div className="gd-tile">
        <span className="tag">try it</span>
        {slider('Account equity', equity, setEquity, 500, 50000, 500, usd)}
        {slider('Risk per trade (1R) — % of account', risk, setRisk, 0.25, 5, 0.25, v => v + '%')}
        {slider("Distance from entry to the trader's stop", stopPct, setStopPct, 1, 20, 0.5, v => v + '%')}
        {slider('Max position size — % of account', capPct, setCapPct, 0, 200, 10, v => (v ? v + '%' : 'no cap'))}
      </div>
      <div className="gd-tile accent gd-out">
        <div>
          <span className="tag">position value</span>
          <div className="gd-big">{usd(notional)}</div>
          <div style={{ fontSize: 12, color: 'var(--text-3)' }}>
            {capped ? 'capped by your max position size' : notional > equity ? 'more than your equity — this uses leverage' : 'what the bot would buy'}
          </div>
        </div>
        <div className={'gd-bar' + (capped ? ' cap' : '')}><i style={{ width: Math.min(100, notional / Math.max(equity, 1) * 50) + '%' }} /></div>
        <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12.5 }}>
          <span style={{ color: 'var(--text-3)' }}>If the stop is hit you lose</span>
          <span className="mono" style={{ color: 'var(--bear)', fontWeight: 600 }}>{usd(lossAtStop)} · {(lossAtStop / equity * 100).toFixed(2)}%</span>
        </div>
        <div style={{ fontSize: 11.5, color: 'var(--text-4)', lineHeight: 1.5 }}>
          A wide stop buys a smaller position, a tight stop a bigger one — the loss at the stop stays your
          chosen 1R{capped ? ', or less when the cap kicks in' : ''}.
        </div>
      </div>
    </div>
  );
}

// ── page ───────────────────────────────────────────────────────────────────
const GUIDE_CHAPTERS = [
  ['what', 'What it does'], ['roles', 'Who sees what'], ['account', 'Your account'], ['connect', 'Connect an exchange'],
  ['follow', 'Follow a strategy'], ['tune', 'Your settings'], ['read', 'Read a strategy page'], ['safety', 'Safety'],
  ['words', 'Words'],
];

// Height of whatever is stuck to the top of the page above the guide (the app's TopBar),
// so the guide's own sticky strip sits under it rather than behind it.
function useStickyOffset(rootRef) {
  useEffectGD(() => {
    const root = rootRef.current;
    if (!root) return undefined;
    const measure = () => {
      let h = 0;
      const main = root.closest('main');
      const bar = main && main.firstElementChild;
      if (bar && bar !== root && getComputedStyle(bar).position === 'sticky') h = bar.getBoundingClientRect().height;
      root.style.setProperty('--gd-top', Math.round(h) + 'px');
    };
    measure();
    const ro = 'ResizeObserver' in window ? new ResizeObserver(measure) : null;
    const main = root.closest('main');
    if (ro && main && main.firstElementChild) ro.observe(main.firstElementChild);
    window.addEventListener('resize', measure);
    return () => { if (ro) ro.disconnect(); window.removeEventListener('resize', measure); };
  }, []);
}

function W_GettingStarted({ user, onNavigate }) {
  const rootRef = useRefGD(null);
  const [active, setActive] = useStateGD('what');
  useReveal(rootRef);
  useStickyOffset(rootRef);

  useEffectGD(() => {
    const root = rootRef.current;
    if (!root || !('IntersectionObserver' in window)) return undefined;
    const io = new IntersectionObserver(entries => {
      entries.forEach(en => { if (en.isIntersecting) setActive(en.target.dataset.ch); });
    }, { rootMargin: '-35% 0px -60% 0px' });
    root.querySelectorAll('[data-ch]').forEach(s => io.observe(s));
    const atEnd = () => {
      if (window.innerHeight + window.scrollY >= document.documentElement.scrollHeight - 4) {
        setActive(GUIDE_CHAPTERS[GUIDE_CHAPTERS.length - 1][0]);
      }
    };
    window.addEventListener('scroll', atEnd, { passive: true });
    return () => { io.disconnect(); window.removeEventListener('scroll', atEnd); };
  }, []);

  const go = id => {
    const el = document.getElementById('gd-' + id);
    if (el) el.scrollIntoView({ behavior: window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth', block: 'start' });
  };
  const role = user ? user.role : null;
  const nextStep = !user ? ['Sign in', 'account'] : role === 'pending' ? ['Check your access', 'account'] : ['Open My account', 'account'];

  return (
    <div className="gd" ref={rootRef}>
      <style>{GUIDE_CSS}</style>
      <div className="gd-grain" aria-hidden="true" />

      <section className="gd-hero">
        <div>
          <div className="gd-kicker gd-reveal">SignalShade · guide</div>
          <h1 className="gd-title gd-reveal d1">A trader posts.<br /><em>Your account follows.</em></h1>
          <p className="gd-lede gd-reveal d2">
            SignalShade reads trade setups from the traders it follows, checks each one is still worth taking,
            and — if you choose — places it on your own exchange account, sized to the risk you set and
            protected by a stop from the first second.
          </p>
          <div className="gd-ctas gd-reveal d3">
            <button className="gd-btn primary" onClick={() => onNavigate(nextStep[1])}>{nextStep[0]} →</button>
            <button className="gd-btn" onClick={() => go('connect')}>How to connect an exchange</button>
          </div>
        </div>
        <div className="gd-reveal d2"><PipelineHero /></div>
      </section>

      <nav className="gd-toc" aria-label="Guide chapters">
        {GUIDE_CHAPTERS.map(([id, label], i) => (
          <button key={id} className={active === id ? 'on' : ''} onClick={() => go(id)}>{String(i + 1).padStart(2, '0')} {label}</button>
        ))}
      </nav>

      <Chapter id="what" num="01" title="What SignalShade does"
               sub="Three jobs, all automatic. You decide only how much to risk and which traders to follow.">
        <div className="gd-grid gd-g3">
          <div className="gd-tile gd-reveal d1"><span className="tag">read</span><h4>📡 Reads every post</h4>
            <p>Each trader's channel is read as it's posted. Setups (coin, entry, stop, targets) are pulled out; updates like "moving stop to entry" or "booking profits" are applied to the open trade.</p></div>
          <div className="gd-tile gd-reveal d2"><span className="tag">check</span><h4>🛡️ Checks it's still good</h4>
            <p>Before anything is placed it asks: is there a stop? Has the stop or first target already been hit? Has price run too far from the entry? Is there still at least 1.5× reward for the risk?</p></div>
          <div className="gd-tile gd-reveal d3"><span className="tag">place</span><h4>⚙️ Places it for you</h4>
            <p>On an account you've set to live, it places the order sized to your risk, with the stop on the exchange and take-profit orders at the trader's targets. In paper mode it only records what would have happened.</p></div>
        </div>
        <p className="gd-p gd-reveal d2" style={{ marginTop: 18 }}>
          Strategies are shown by a public name (<b>Strategy A</b>, <b>Strategy B</b>…), never the trader's real name.
          Every strategy page shows its full record — open trades, history and results — so you can judge one
          before risking anything on it.
        </p>
      </Chapter>

      <Chapter id="roles" num="02" title="Who sees what"
               sub="Everyone signs in the same way. What you can do depends on the access you've been given.">
        <div className="gd-roles gd-reveal d1">
          {[
            ['pending', 'Pending approval', 'just signed up', ['Research pages and AI Scan results', 'This guide', 'Waits for access to be approved']],
            ['user', 'User', 'shown as "Free tier"', ['Every strategy: ledger, history, results', 'Connect an exchange account', 'Choose which strategies it follows', 'Notifications, Compare strategies, AI Scan']],
            ['arena', 'Member', 'user + read-only extras', ['Everything a user can do', "The trader's own messages and commentary", 'The Telegram feed', 'Why each trade was taken or skipped (decision history)']],
          ].map(([k, name, who, items]) => (
            <div key={k} className={'gd-role' + (role === k ? ' me' : '')}>
              <h4>{name}{role === k && <span className="you">YOU</span>}</h4>
              <div className="who">{who}</div>
              <ul>{items.map(x => <li key={x}>{x}</li>)}</ul>
            </div>
          ))}
        </div>
        <p className="gd-p gd-reveal d2" style={{ marginTop: 16 }}>
          <b>Members</b> are people who already have their own access to the traders' Telegram groups — they're
          subscribed to those groups themselves. Member access only lets you read here what you can already read
          there, next to the trades it produced; it isn't a way to get the groups' content.
        </p>
        <p className="gd-p gd-reveal d2" style={{ marginTop: 10 }}>
          A member's extras are <b>read-only</b>: the messages behind every trade and the reasons it was taken or
          skipped. Look for the <Q>↗</Q> next to a trade's open time, the <Q>timeline</Q> button in History, and the
          <b> Signal feed</b>, <b>Trader commentary</b> and <b>Decision audit</b> panels on each strategy page.
        </p>
      </Chapter>

      <Chapter id="account" num="03" title="Set up your account"
               sub="Sign in once; your access is approved (or you were invited, and you're in straight away).">
        <div className="gd-steps">
          <div className="gd-step gd-reveal d1"><div><h5>Sign in</h5>
            <p>Open <b>My account</b> at the bottom of the sidebar and sign in. Your first sign-in creates your account.</p></div></div>
          <div className="gd-step gd-reveal d2"><div><h5>Wait for approval — or skip it with an invite</h5>
            <p>New accounts show <Q>Waiting for approval</Q> until access is approved; you'll get an email when it is.
              If you were <b>invited</b>, signing in with the invited email address gives you access immediately.</p></div></div>
          <div className="gd-step gd-reveal d3"><div><h5>Turn on notifications (optional)</h5>
            <p>At the top of My account, <Q>Browser notifications</Q> → <Q>Turn on</Q>. You'll be told when an order fills,
              is changed or closes. A filled order also sends you an email receipt.</p></div></div>
          <div className="gd-step gd-reveal d4"><div><h5>Look around first</h5>
            <p>Nothing needs connecting to browse: every strategy in the sidebar already shows its trades and results.</p></div></div>
        </div>
        <Shot src="guide/account-top.png" path="#/account" alt="Top of My account: notifications, the account tab, Trading active and open positions"
              caption="My account — notifications, your account's tab, the Stop trading switch and its open positions." />
      </Chapter>

      <Chapter id="connect" num="04" title="Connect an exchange"
               sub="Bybit is the exchange that can copy trades. Binance, Coinbase and Kraken can be connected as read-only portfolios.">
        <div className="gd-grid gd-g2" style={{ alignItems: 'start' }}>
          <div className="gd-steps">
            <div className="gd-step gd-reveal d1"><div><h5>Create a trade-only API key on Bybit</h5>
              <p>In Bybit's API management, allow <b>Contracts / Derivatives — Orders and Positions</b> only, and restrict
                it to the IP address shown in the app. For practice, switch Bybit to <b>Demo Trading</b> first and create the key there.</p></div></div>
            <div className="gd-step gd-reveal d2"><div><h5>Add it in My account</h5>
              <p>Choose Bybit, give the account a <b>Name</b>, pick <Q>Demo (practice funds)</Q> or <Q>Real money</Q>, paste the
                <b> API key</b> and <b>API secret</b>, and press <Q>Connect</Q>.</p></div></div>
            <div className="gd-step gd-reveal d3"><div><h5>It's checked with Bybit</h5>
              <p>You'll see <Q>checking key</Q>, then <Q>connected</Q>. If you see <Q>key problem</Q>, use
                <Q>Replace key</Q>. Nothing is traded until the key is checked.</p></div></div>
          </div>
          <div className="gd-grid" style={{ gap: 10 }}>
            <div className="gd-note warn gd-reveal d2"><span className="ic">⛔</span><div>
              <b>Never enable withdrawals or transfers</b> on the key. The app refuses any key that can withdraw — it only
              ever needs to place and manage trades.</div></div>
            <div className="gd-note gd-reveal d3"><span className="ic">🔐</span><div>
              The key is encrypted the moment it reaches the server and is never shown again — only its last 4 characters.</div></div>
            <div className="gd-note tip gd-reveal d4"><span className="ic">💡</span><div>
              Start on a <b>Demo</b> account. It uses real market prices with practice funds, so you see exactly how a
              strategy behaves without risking money.</div></div>
          </div>
        </div>
        <Shot src="guide/account.png" path="#/account" alt="Account settings and the Strategies card on My account"
              caption="Once connected: the account's settings (paper or live, limits) and the strategies it follows." />
      </Chapter>

      <Chapter id="follow" num="05" title="Follow a strategy"
               sub="Following happens on your account, not on the strategy page — so each account can follow different traders.">
        <div className="gd-steps">
          <div className="gd-step gd-reveal d1"><div><h5>Open the account's Strategies card</h5>
            <p>In My account, under your Bybit account: <i>"Choose which strategies this account copies and how big each trade is."</i></p></div></div>
          <div className="gd-step gd-reveal d2"><div><h5>Tick a strategy, set the risk, Save</h5>
            <p>It shows <Q>following since …</Q>. Only setups posted <b>after</b> you turn a strategy on are copied — nothing from before.</p></div></div>
          <div className="gd-step gd-reveal d3"><div><h5>Stay in paper mode for a while</h5>
            <p>New accounts start on <Q>Paper — nothing is sent to Bybit</Q>. Signals are still recorded, so you can see what would have happened.</p></div></div>
          <div className="gd-step gd-reveal d4"><div><h5>Go live when you're ready</h5>
            <p><Q>Go live…</Q> asks you to type <b>LIVE</b>. From that moment orders are placed on this account, and again only setups
              posted after that moment are copied. <Q>Switch to paper</Q> stops new orders any time; open positions stay open.</p></div></div>
        </div>
      </Chapter>

      <Chapter id="tune" num="06" title="Your settings"
               sub="Four numbers decide how big each trade is and when the account stops taking new ones.">
        <SizingCalc />
        <div className="gd-grid gd-g2" style={{ marginTop: 14 }}>
          <div className="gd-tile gd-reveal d1"><span className="tag">per strategy</span><h4>Risk per trade (1R)</h4>
            <p>What you lose if a trade hits its stop, as a % of the account. <b>0.5–2% is typical.</b> The position size is
              worked out from it and the distance to the trader's stop.</p></div>
          <div className="gd-tile gd-reveal d2"><span className="tag">per strategy · optional</span><h4>Max position size</h4>
            <p>Caps a position's value as a % of the account. Leave it empty for no cap; above 100% means leverage.</p></div>
          <div className="gd-tile gd-reveal d3"><span className="tag">per account</span><h4>Max open positions</h4>
            <p>New setups are skipped while this many are open (default 3). It limits live trading; paper keeps recording everything.</p></div>
          <div className="gd-tile gd-reveal d4"><span className="tag">per account · optional</span><h4>Daily loss limit</h4>
            <p>After losing this % of the account in a day, no new trades until tomorrow (UTC).</p></div>
        </div>
        <div className="gd-note gd-reveal d2" style={{ marginTop: 14 }}><span className="ic">⏹</span><div>
          <b>Stop trading</b> at the top of an account is its kill switch: no new entries on that account, open positions stay open.
          <Q>Resume trading</Q> turns it back on. To close a position right away, use <Q>Close</Q> on it — that sends a market order and can't be undone.</div></div>
      </Chapter>

      <Chapter id="read" num="07" title="Read a strategy page"
               sub="Each strategy in the sidebar has the same layout. The small gold number next to it counts new signals since you last looked.">
        <div className="gd-grid gd-g3">
          <div className="gd-tile gd-reveal d1"><span className="tag">top</span><h4>Your account P&amp;L</h4>
            <p>Unrealized (open trades, marked to the live price) and realized for the period you pick, plus entry slippage — how far
              your fills were from the trader's stated prices.</p></div>
          <div className="gd-tile gd-reveal d2"><span className="tag">tabs</span><h4>Active trades · History · Report</h4>
            <p>What's open now, everything that has ended (reached target, stopped out, entry never reached…), and a results summary:
              win rate, average R, expectancy, best and worst.</p></div>
          <div className="gd-tile gd-reveal d3"><span className="tag">filters</span><h4>Narrow it down</h4>
            <p><Q>My trades only</Q>, Demo / Real money, long / short, a period from 1W to All. Rows you don't hold are dimmed and say
              <i> "Not on your account"</i>.</p></div>
        </div>
        <p className="gd-p gd-reveal d2" style={{ marginTop: 16 }}>
          <b>Notify me</b> at the top of a strategy chooses whether that strategy's fills and closes notify you (it's on by default for
          strategies you follow). <b>Compare strategies</b> puts them side by side in R — risk units — so results don't depend on account size.
        </p>
      </Chapter>

      <Chapter id="safety" num="08" title="How your money is protected"
               sub="The rules every trade goes through, in plain words.">
        <div className="gd-grid gd-g2">
          <div className="gd-tile accent gd-reveal d1"><h4>No stop, no order</h4>
            <p>The stop-loss goes on the exchange together with the entry. If a setup has no stop, it isn't placed live.</p></div>
          <div className="gd-tile accent gd-reveal d2"><h4>Late setups are skipped</h4>
            <p>If the stop or first target already traded before we saw the post, or price ran too far from the entry, or there's less than
              1.5× reward left for the risk, a live account skips it. Paper still records it so you can compare.</p></div>
          <div className="gd-tile accent gd-reveal d3"><h4>Limit entries wait — for a while</h4>
            <p>When a trader gives a price to buy at, the order rests there as a limit (<Q>pending entry</Q>) until price reaches it.
              An order that's still unfilled after 7 days is cancelled.</p></div>
          <div className="gd-tile accent gd-reveal d4"><h4>Paper first, live by choice</h4>
            <p>Every account starts in paper mode. Going live needs a checked key and typing LIVE; real-money accounts also need real
              trading enabled on the platform.</p></div>
        </div>
        <div className="gd-note gd-reveal d2" style={{ marginTop: 14 }}><span className="ic">⚖️</span><div>
          Trading is risky and past results don't promise future ones. SignalShade copies other people's trade ideas; it doesn't give
          financial advice. Only risk what you can afford to lose.</div></div>
      </Chapter>

      <Chapter id="words" num="09" title="Words you'll see">
        <dl className="gd-gloss gd-reveal d1">
          {[
            ['1R', 'One unit of risk — what a trade loses if it hits its stop. A +2R trade made twice what it risked.'],
            ['CMP', '"Current market price." "Entry: CMP" means buy at the price when the post arrives.'],
            ['Paper', 'Recorded but not sent to the exchange. Shows what would have happened.'],
            ['Demo / Real money', 'Bybit practice funds, or real funds. Separate from paper vs live.'],
            ['Pending entry', "A limit order waiting for price to reach the trader's level."],
            ['Matched / Drift', 'Matched: an account really holds the trade. Drift: it\'s in the ledger only — no account holds it.'],
            ['Slippage', "How far your actual fill was from the trader's stated price."],
            ['Expired', "The trader went silent on the trade for about two months, so it was closed out."],
          ].map(([w, d]) => <div key={w}><dt>{w}</dt><dd>{d}</dd></div>)}
        </dl>
        <div className="gd-ctas gd-reveal d2" style={{ marginTop: 26 }}>
          <button className="gd-btn primary" onClick={() => onNavigate(nextStep[1])}>{nextStep[0]} →</button>
          <button className="gd-btn" onClick={() => onNavigate('compare')}>Compare strategies</button>
          <button className="gd-btn" onClick={() => onNavigate('overview')}>Back to Overview</button>
        </div>
      </Chapter>
    </div>
  );
}
