// app.jsx — v2 top-level composition.
//
// New shape, same content. The page now reads (top→bottom):
//
//   <Hero/>                        slim, one sentence
//   <VarsBuilder/>                 sticky · type your branch once
//   <TwoEntryPoints/>              who started the work? (DIY vs. Claude)
//   <SituationPicker/>             6 tiles by "what just happened"
//   <ErrorSearch/>                 / to focus · indexes every errorTable row
//   {LaneCard × 3}                 same content as v1, with live variables
//   <DecisionPicker/>              demoted — matrix + flowchart for the curious
//   <ConceptsPanel/>                                       (in right rail)
//   <AntiPatterns/> + <Footer/>
//
// data.jsx and parts.jsx are untouched. Variable substitution is wired by
// wrapping every CodeBlock in a tiny <TCode> that runs the raw string through
// useTemplate(), so existing snippets like `git switch -c <branch>` light up.

const { useState: vS, useEffect: vE, useMemo: vM, useCallback: vCb, useRef: vR } = React;

// ----- tiny adapter: CodeBlock + variable interpolation ----------------------
function TCode({ code, ...rest }) {
  const resolved = window.useTemplate(code);
  return <window.CodeBlock code={resolved} {...rest} />;
}

// ============================================================================
// Hero
// ============================================================================
function Hero() {
  return (
    <header style={{ padding: "32px 0 16px" }}>
      <div style={{
        display: "flex", alignItems: "baseline", gap: 12, flexWrap: "wrap",
        fontFamily: "Geist Mono, monospace", fontSize: 11,
        letterSpacing: "0.14em", textTransform: "uppercase",
        color: "var(--ink-faint)",
      }}>
        <span>Expo + Claude Code</span>
        <span style={{ width: 4, height: 4, borderRadius: "50%", background: "var(--ink-faint)" }}/>
        <span>Workflow Cheatsheet</span>
        <span style={{ width: 4, height: 4, borderRadius: "50%", background: "var(--ink-faint)" }}/>
        <span>v2</span>
      </div>
      <h1 style={{
        margin: "10px 0 6px",
        fontFamily: "Bricolage Grotesque, sans-serif",
        fontWeight: 700, fontSize: "clamp(32px, 4.6vw, 52px)",
        lineHeight: 1.02, letterSpacing: "-0.02em",
        textWrap: "balance",
      }}>
        Pick the right lane <span style={{ fontFamily: "Caveat, cursive", fontWeight: 600, color: "var(--ink-faint)", fontSize: "0.7em" }}>before</span> you start typing.
      </h1>
      <p style={{ maxWidth: 680, color: "var(--ink-soft)", fontSize: 16, margin: "8px 0 0" }}>
        Type your branch name once <span style={{ color: "var(--ink)", fontFamily: "Geist Mono, monospace" }}>↓</span> · pick the situation that matches yours · copy and run. The full lane reference lives below.
      </p>
    </header>
  );
}

// ============================================================================
// LaneCard — same as v1 but mode is externally controllable and CodeBlock
// usages go through <TCode> so variables interpolate.
// ============================================================================
function LaneCard({ lane, externalMode, onSeen }) {
  const hasModes = Array.isArray(lane.modes) && lane.modes.length > 0;
  const [modeId, setMode] = vS(hasModes ? lane.modes[0].id : null);
  const [tab, setTab] = vS("steps");

  // Honour externally requested mode (from situation picker / error search)
  vE(() => {
    if (hasModes && externalMode && externalMode !== modeId) {
      setMode(externalMode);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [externalMode]);

  const tabs = [
    { id: "steps", label: "Steps" },
    { id: "diagram", label: "Diagram" },
    { id: "gotchas", label: "Gotchas" },
    { id: "commands", label: "Commands" },
  ];
  const mode = hasModes ? lane.modes.find((m) => m.id === modeId) : null;
  const view = hasModes ? { ...lane, ...mode } : lane;
  const accent = lane.accent;

  return (
    <section id={lane.id} style={{
      background: "var(--paper)", borderRadius: 20,
      border: "1px solid var(--rule)",
      borderTop: `4px solid ${accent}`,
      overflow: "hidden",
      boxShadow: "0 1px 0 rgba(255,255,255,0.6) inset, 0 10px 30px -22px rgba(0,0,0,0.25)",
    }}>
      <div style={{ padding: "22px 24px 0", display: "flex", flexWrap: "wrap", gap: 18, alignItems: "flex-start" }}>
        <window.LaneBadge n={lane.n} accent={accent} accentBg={lane.accentBg} accentInk={lane.accentInk} size={44}/>
        <div style={{ flex: "1 1 320px", minWidth: 0 }}>
          <div style={{
            fontFamily: "Geist Mono, monospace", fontSize: 11,
            letterSpacing: "0.12em", textTransform: "uppercase",
            color: lane.accentInk,
          }}>Lane {lane.n} · {lane.short} <span style={{ color: "var(--ink-faint)" }}>· {view.tag}</span></div>
          <h2 style={{
            margin: "4px 0 0",
            fontFamily: "Bricolage Grotesque, sans-serif",
            fontSize: 26, fontWeight: 700, lineHeight: 1.15,
            letterSpacing: "-0.01em",
          }}>{view.title}</h2>
          <p style={{ margin: "8px 0 0", color: "var(--ink-soft)", fontSize: 14.5 }}>
            <strong style={{ color: "var(--ink)" }}>Use when:</strong> {view.when}
          </p>
          {view.whyNot && (
            <p style={{ margin: "6px 0 0", color: "var(--ink-soft)", fontSize: 14.5 }}>
              <strong style={{ color: "var(--ink)" }}>Why not a PR?</strong> {view.whyNot}
            </p>
          )}
        </div>
        <div>
          <window.Tabs tabs={tabs} value={tab} onChange={setTab} accent={lane.accentInk}/>
        </div>
      </div>

      {hasModes && (
        <div style={{ padding: "18px 24px 0" }}>
          <window.ModeToggle
            modes={lane.modes}
            value={modeId}
            onChange={setMode}
            accent={lane.accent}
            accentBg={lane.accentBg}
            accentInk={lane.accentInk}
          />
        </div>
      )}

      <div style={{ padding: "20px 24px 24px" }}>
        {tab === "steps"    && <StepsView    lane={view} />}
        {tab === "diagram"  && <DiagramView  lane={view} />}
        {tab === "gotchas"  && <GotchasView  lane={view} />}
        {tab === "commands" && <CommandsView lane={view} />}
      </div>
    </section>
  );
}

function StepsView({ lane }) {
  return (
    <div>
      {lane.painPoint && (
        <div style={{
          marginBottom: 18, padding: "14px 16px",
          background: lane.accentBg, border: `1px solid ${lane.accent}`,
          borderRadius: 12,
        }}>
          <div style={{ fontWeight: 700, color: lane.accentInk, fontSize: 14 }}>{lane.painPoint.h}</div>
          <div style={{ color: "var(--ink-soft)", fontSize: 13.5, marginTop: 4 }}>{lane.painPoint.b}</div>
          <div style={{ marginTop: 12, overflowX: "auto" }}>
            <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13 }}>
              <thead>
                <tr>
                  <Th>Approach</Th>
                  <Th>One-time setup</Th>
                  <Th>Per-worktree cost</Th>
                  <Th>Notes</Th>
                </tr>
              </thead>
              <tbody>
                {lane.painPoint.table.map((r, i) => (
                  <tr key={i}>
                    <Td style={{ fontWeight: 600 }}>{r.approach}</Td>
                    <Td>{r.setup}</Td>
                    <Td style={{ fontFamily: "Geist Mono, monospace", fontSize: 12.5 }}>{r.cost}</Td>
                    <Td style={{ color: "var(--ink-soft)" }}>{r.note}</Td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      )}
      <ol style={{ listStyle: "none", padding: 0, margin: 0, display: "grid", gap: 14 }}>
        {lane.steps.map((s, i) => (
          <li key={i} style={{ display: "grid", gridTemplateColumns: "32px 1fr", gap: 14 }}>
            <div style={{
              width: 28, height: 28, borderRadius: "50%",
              background: lane.accentBg, color: lane.accentInk,
              border: `1.5px solid ${lane.accent}`,
              display: "grid", placeItems: "center",
              fontFamily: "Geist Mono, monospace", fontWeight: 700, fontSize: 13,
            }}>{i + 1}</div>
            <div style={{ minWidth: 0 }}>
              <div style={{ fontWeight: 600, fontSize: 14.5, marginBottom: 6 }}>{s.h}</div>
              <TCode code={s.c} accent={lane.accent} small label={`step ${i + 1}`} />
            </div>
          </li>
        ))}
      </ol>
      {lane.stuck && (
        <div style={{ marginTop: 22 }}>
          <div style={{ fontWeight: 700, fontSize: 15, marginBottom: 4, color: lane.accentInk }}>{lane.stuck.h}</div>
          <div style={{ color: "var(--ink-soft)", fontSize: 13.5, marginBottom: 8 }}>{lane.stuck.b}</div>
          <TCode code={lane.stuck.c} accent={lane.accent} label="universal fixer" />
        </div>
      )}
    </div>
  );
}

function DiagramView({ lane }) {
  const D =
    lane.diagram === "timeline-l1" ? <window.DiagramTimeline/>
    : lane.diagram === "parallel-l2" ? <window.DiagramParallel/>
    : lane.diagram === "review-l2" ? <window.DiagramReview/>
    : lane.diagram === "lifecycle-l3" ? <window.DiagramLifecycle/>
    : null;
  const caption =
    lane.diagram === "timeline-l1" ? "Branch born off dev, one commit, merged fast-forward, branch deleted."
    : lane.diagram === "parallel-l2" ? "Main repo and each worktree work in parallel. PRs are the one true path back to dev."
    : lane.diagram === "review-l2" ? "Cloud agent path — your main repo briefly diverts to the agent's branch in place, verifies, then returns to dev. Local agent path is shorter: the branch is already in .claude/worktrees/<name>, so you cd in instead of checking out."
    : "active → (agent) locked → unlock → active → remove. The dashed path is the trap — rm -rf leaves an orphan only prune can fix.";
  return (
    <div>
      <div style={{
        background: "var(--cream-deep)", borderRadius: 12,
        border: "1px solid var(--rule)", padding: 18,
      }}>{D}</div>
      <div style={{ color: "var(--ink-faint)", fontSize: 12.5, marginTop: 10, fontStyle: "italic" }}>{caption}</div>
    </div>
  );
}

function GotchasView({ lane }) {
  return (
    <div>
      {lane.errorTable && (
        <div style={{ marginBottom: 18, overflowX: "auto" }}>
          <div style={{ fontWeight: 700, fontSize: 15, marginBottom: 8 }}>Error → cause → fix</div>
          <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13 }}>
            <thead>
              <tr><Th>Symptom</Th><Th>Cause</Th><Th>Fix</Th></tr>
            </thead>
            <tbody>
              {lane.errorTable.map((r, i) => (
                <tr key={i}>
                  <Td style={{ fontFamily: "Geist Mono, monospace", color: lane.accentInk, fontWeight: 600 }}>{r.sym}</Td>
                  <Td style={{ color: "var(--ink-soft)" }}>{r.cause}</Td>
                  <Td style={{ fontFamily: "Geist Mono, monospace", fontSize: 12.5 }}>{r.fix}</Td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}
      <div style={{ display: "grid", gap: 12 }}>
        {lane.gotchas.map((g, i) => (
          <div key={i} style={{
            padding: "12px 14px", borderRadius: 12,
            background: "var(--cream-deep)", border: `1px solid var(--rule)`,
            borderLeft: `3px solid ${lane.accent}`,
          }}>
            <div style={{ fontWeight: 600, fontSize: 14, color: "var(--ink)" }}>{g.h}</div>
            {g.b && <div style={{ color: "var(--ink-soft)", fontSize: 13.5, marginTop: 4 }}>{g.b}</div>}
            {g.code && <div style={{ marginTop: 10 }}><TCode code={g.code} accent={lane.accent} small label="snippet"/></div>}
          </div>
        ))}
      </div>
    </div>
  );
}

function CommandsView({ lane }) {
  return (
    <div>
      <div style={{ color: "var(--ink-soft)", fontSize: 13.5, marginBottom: 10 }}>
        Quick reference — placeholders update from the toolbar above.
      </div>
      <TCode code={lane.quick} accent={lane.accent} label={`lane ${lane.n} quick ref`} />
    </div>
  );
}

// ============================================================================
// Decision matrix + flowchart — demoted to "More references"
// ============================================================================
function MoreReferences({ goto }) {
  const { MATRIX } = window.CHEAT;
  const [open, setOpen] = vS(false);
  return (
    <section id="more-refs" style={{ marginTop: 28 }}>
      <button onClick={() => setOpen(o => !o)} style={{
        display: "flex", alignItems: "center", gap: 10,
        background: "var(--paper)", border: "1px solid var(--rule)",
        borderRadius: 12, padding: "12px 16px", width: "100%",
        cursor: "pointer", textAlign: "left",
      }}>
        <span style={{
          width: 20, height: 20, borderRadius: 4,
          border: "1px solid var(--rule-strong)", display: "grid", placeItems: "center",
          transition: "transform .2s", transform: open ? "rotate(45deg)" : "none",
          color: "var(--ink-soft)",
        }}>+</span>
        <span style={{ fontWeight: 600, fontSize: 14 }}>More references — decision matrix &amp; flowchart</span>
        <span style={{ marginLeft: "auto", color: "var(--ink-faint)", fontSize: 12 }}>{open ? "hide" : "show"}</span>
      </button>
      {open && (
        <div style={{
          display: "grid", gridTemplateColumns: "minmax(0, 1.05fr) minmax(0, 1fr)",
          gap: 18, marginTop: 14,
        }} className="cheat-refgrid">
          <div style={{ background: "var(--paper)", border: "1px solid var(--rule)", borderRadius: 16, padding: 18 }}>
            <Eyebrow>Pick by change size</Eyebrow>
            <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 14 }}>
              <thead>
                <tr><Th>Change size</Th><Th>Parallel agent?</Th><Th style={{ textAlign: "right" }}>Go to</Th></tr>
              </thead>
              <tbody>
                {MATRIX.map((r, i) => {
                  const L = window.CHEAT.LANES[r.lane - 1];
                  return (
                    <tr key={i}
                      onClick={() => goto({ laneId: L.id })}
                      style={{ cursor: "pointer" }}
                      onMouseEnter={(e) => e.currentTarget.style.background = L.accentBg}
                      onMouseLeave={(e) => e.currentTarget.style.background = ""}
                    >
                      <Td>{r.size}</Td>
                      <Td style={{ color: "var(--ink-soft)", fontFamily: "Geist Mono, monospace", fontSize: 12.5 }}>{r.parallel}</Td>
                      <Td style={{ textAlign: "right" }}>
                        <window.LaneChip lane={r.lane} onClick={(id) => goto({ laneId: id })} />
                      </Td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
          <div style={{ background: "var(--paper)", border: "1px solid var(--rule)", borderRadius: 16, padding: 18 }}>
            <Eyebrow>Or follow the questions</Eyebrow>
            <window.Flowchart goto={(id) => goto({ laneId: id })} />
          </div>
        </div>
      )}
    </section>
  );
}

function Eyebrow({ children }) {
  return <div style={{
    fontFamily: "Geist Mono, monospace", fontSize: 10.5,
    letterSpacing: "0.14em", textTransform: "uppercase",
    color: "var(--ink-faint)", marginBottom: 10,
  }}>{children}</div>;
}
function Th({ children, style }) {
  return <th style={{
    textAlign: "left", padding: "10px 12px",
    borderBottom: "1px solid var(--rule-strong)",
    fontFamily: "Geist Mono, monospace", fontSize: 11,
    letterSpacing: "0.1em", textTransform: "uppercase",
    color: "var(--ink-faint)", fontWeight: 600, ...style,
  }}>{children}</th>;
}
function Td({ children, style }) {
  return <td style={{
    padding: "12px 12px", borderBottom: "1px solid var(--rule)",
    verticalAlign: "middle", ...style,
  }}>{children}</td>;
}

// ============================================================================
// ConceptsPanel — unchanged from v1
// ============================================================================
function ConceptsPanel() {
  const [openId, setOpenId] = vS(null);
  const { CONCEPTS } = window.CHEAT;
  return (
    <aside id="concepts" style={{
      background: "var(--paper)", borderRadius: 16,
      border: "1px solid var(--rule)",
      padding: "16px 18px 6px",
      position: "sticky", top: 76,
      maxHeight: "calc(100vh - 92px)",
      overflow: "auto",
    }}>
      <Eyebrow>Concepts</Eyebrow>
      <div style={{
        fontFamily: "Bricolage Grotesque, sans-serif",
        fontWeight: 700, fontSize: 18, margin: "2px 0 6px",
      }}>The pieces behind the lanes</div>
      <div style={{ color: "var(--ink-soft)", fontSize: 12.5, marginBottom: 6 }}>
        Tap any term to expand.
      </div>
      {CONCEPTS.map((c) => (
        <window.Concept key={c.id} c={c}
          open={openId === c.id}
          onToggle={() => setOpenId(openId === c.id ? null : c.id)} />
      ))}
    </aside>
  );
}

function AntiPatterns() {
  const { ANTI } = window.CHEAT;
  return (
    <section id="anti" style={{ marginTop: 28 }}>
      <div style={{ background: "var(--paper)", border: "1px solid var(--rule)", borderRadius: 16, padding: 18 }}>
        <Eyebrow>Don't</Eyebrow>
        <div style={{
          fontFamily: "Bricolage Grotesque, sans-serif", fontWeight: 700, fontSize: 22,
          marginBottom: 14,
        }}>Anti-patterns</div>
        <ul style={{ listStyle: "none", padding: 0, margin: 0, display: "grid", gap: 8 }}>
          {ANTI.map((a, i) => (
            <li key={i} style={{
              display: "grid", gridTemplateColumns: "24px 1fr", gap: 10,
              padding: "10px 12px", borderRadius: 10, background: "var(--cream-deep)",
            }}>
              <div style={{
                width: 22, height: 22, borderRadius: "50%",
                background: "var(--paper)", color: "var(--l3)",
                border: "1.5px solid var(--l3)",
                display: "grid", placeItems: "center",
                fontWeight: 700, fontSize: 14, lineHeight: 1,
              }}>×</div>
              <div>
                <span style={{ fontFamily: "Geist Mono, monospace", fontSize: 13, color: "var(--ink)", fontWeight: 600 }}>{a.h}</span>
                <span style={{ color: "var(--ink-soft)", fontSize: 13.5 }}> — {a.b}</span>
              </div>
            </li>
          ))}
        </ul>
      </div>
    </section>
  );
}

function Footer() {
  return (
    <footer style={{
      marginTop: 40, padding: "20px 0 40px",
      borderTop: "1px solid var(--rule)",
      display: "flex", justifyContent: "space-between", flexWrap: "wrap", gap: 12,
      color: "var(--ink-faint)", fontFamily: "Geist Mono, monospace", fontSize: 11,
      letterSpacing: "0.08em", textTransform: "uppercase",
    }}>
      <div>Expo + Claude · Workflow Cheatsheet · v2</div>
      <div style={{ display: "flex", gap: 14 }}>
        <a href="#lane-1" style={{ textDecoration: "none", color: "var(--l1-ink)" }}>Lane 1</a>
        <a href="#lane-2" style={{ textDecoration: "none", color: "var(--l2-ink)" }}>Lane 2</a>
        <a href="#lane-3" style={{ textDecoration: "none", color: "var(--l3-ink)" }}>Lane 3</a>
        <a href="#concepts" style={{ textDecoration: "none", color: "inherit" }}>Concepts</a>
      </div>
    </footer>
  );
}

// ============================================================================
// App
// ============================================================================
function App() {
  const { LANES } = window.CHEAT;
  const errorRef = vR(null);

  // pending external mode per lane id (set by situation picker / error search)
  const [pendingModes, setPendingModes] = vS({});

  const goto = vCb((arg) => {
    // arg can be a string id (legacy) or { laneId, modeId, action }
    const target = typeof arg === "string" ? { laneId: arg } : (arg || {});
    if (target.action === "focus-error") {
      errorRef.current && errorRef.current.focus();
      const el = document.getElementById("error-search");
      if (el) el.scrollIntoView({ behavior: "smooth", block: "start" });
      return;
    }
    if (target.action === "scroll-flowchart") {
      const el = document.getElementById("more-refs");
      if (el) {
        el.scrollIntoView({ behavior: "smooth", block: "start" });
        // auto-open
        const btn = el.querySelector("button");
        if (btn && btn.getAttribute("aria-expanded") !== "true") btn.click();
      }
      return;
    }
    if (target.modeId) {
      setPendingModes((m) => ({ ...m, [target.laneId]: target.modeId }));
    }
    const el = document.getElementById(target.laneId);
    if (el) el.scrollIntoView({ behavior: "smooth", block: "start" });
  }, []);

  // active-lane highlight in floating rail
  const [active, setActive] = vS(null);
  vE(() => {
    const ids = LANES.map((l) => l.id);
    const obs = new IntersectionObserver(
      (entries) => entries.forEach((e) => { if (e.isIntersecting) setActive(e.target.id); }),
      { rootMargin: "-30% 0px -60% 0px", threshold: 0 }
    );
    ids.forEach((id) => { const el = document.getElementById(id); if (el) obs.observe(el); });
    return () => obs.disconnect();
  }, []);

  return (
    <window.VarsProvider>
      <window.VarsBuilder />
      <div style={{ maxWidth: 1240, margin: "0 auto", padding: "0 28px 0" }}>
        <Hero />
        <window.TwoEntryPoints goto={goto} />
        <window.SituationPicker onGoto={goto} onAction={(a) => goto({ action: a })} />
        <window.ErrorSearch onGoto={goto} mountRef={errorRef} />

        <div style={{
          display: "grid",
          gridTemplateColumns: "minmax(0, 1fr) 320px",
          gap: 28, alignItems: "start",
        }} className="cheat-grid">
          <div style={{ display: "grid", gap: 24, minWidth: 0 }}>
            {LANES.map((l) => (
              <LaneCard key={l.id} lane={l} externalMode={pendingModes[l.id]} />
            ))}
            <MoreReferences goto={goto} />
            <AntiPatterns />
          </div>
          <ConceptsPanel />
        </div>

        <Footer />

        {/* Floating lane rail */}
        <div style={{
          position: "fixed", left: 14, top: "50%", transform: "translateY(-50%)",
          display: "flex", flexDirection: "column", gap: 8, zIndex: 5,
        }} className="cheat-rail">
          {LANES.map((l) => {
            const on = active === l.id;
            return (
              <button key={l.id} onClick={() => goto({ laneId: l.id })}
                title={`Lane ${l.n} · ${l.short}`}
                style={{
                  width: on ? 38 : 30, height: on ? 38 : 30, borderRadius: "50%",
                  background: on ? l.accent : l.accentBg,
                  color: on ? "white" : l.accentInk,
                  border: `1.5px solid ${l.accent}`,
                  cursor: "pointer", fontWeight: 700,
                  fontFamily: "Bricolage Grotesque, sans-serif",
                  fontSize: on ? 16 : 13,
                  display: "grid", placeItems: "center",
                  transition: "all .18s",
                  boxShadow: on ? `0 6px 18px -8px ${l.accent}` : "none",
                }}>
                {l.n}
              </button>
            );
          })}
        </div>

        <style>{`
          @media (max-width: 980px) {
            .cheat-grid { grid-template-columns: 1fr !important; }
            .cheat-grid > aside { position: static !important; max-height: none !important; }
            .cheat-refgrid { grid-template-columns: 1fr !important; }
          }
          @media (max-width: 820px) {
            .cheat-sitgrid { grid-template-columns: repeat(2, 1fr) !important; }
            .cheat-twoentries { grid-template-columns: 1fr !important; }
          }
          @media (max-width: 1180px) {
            .cheat-rail { display: none !important; }
          }
          @media (max-width: 540px) {
            .cheat-sitgrid { grid-template-columns: 1fr !important; }
          }
        `}</style>
      </div>
    </window.VarsProvider>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
