/* eslint-disable */
const { useState, useEffect, useMemo, useRef } = React;

// ---------- Atoms ----------
function Kicker({ children }) {
  return <div className="kicker">{children}</div>;
}

function StepNumber({ n }) {
  return <span className="step-num" aria-hidden="true">{String(n).padStart(2,"0")}</span>;
}

function Code({ children }) {
  return <code className="ic">{children}</code>;
}

/* i18n prose may carry [label](/root/absolute) links; everything else is text.
   Never call it inside an element that is itself a link: nested anchors. */
function rich(text) {
  if (typeof text !== "string" || text.indexOf("](") === -1) return text;
  const re = /\[([^\]]+)\]\(([^)\s]+)\)/g;
  const out = [];
  let last = 0, m;
  while ((m = re.exec(text))) {
    if (m.index > last) out.push(text.slice(last, m.index));
    out.push(<a key={m.index} href={m[2]}>{m[1]}</a>);
    last = m.index + m[0].length;
  }
  if (last < text.length) out.push(text.slice(last));
  return out;
}

function Cmd({ lines, t }) {
  return (
    <pre className="cmd" aria-label="terminal command">
      {lines.map((l, i) => (
        <span key={i} className="cmd-line">
          <span className="cmd-gutter">{String(i+1).padStart(2,"0")}</span>
          <span className="cmd-prompt">$</span>
          <span className="cmd-text">{l}</span>
        </span>
      ))}
    </pre>
  );
}

function Callout({ kind, title, children }) {
  return (
    <div className={`callout callout-${kind}`}>
      <div className="callout-tag">{title}</div>
      <div className="callout-body">{children}</div>
    </div>
  );
}

function ScreenshotSlot({ id, label, caption, t, aspect = "16/10", src, fit }) {
  return (
    <figure className={`ss-figure ${src ? "ss-has-image" : ""}`} style={{ "--aspect": aspect }}>
      <figcaption className="ss-caption">
        <span className="ss-marker">{src ? "SCREENSHOT" : "PLACEHOLDER"}</span>
        <span className="ss-label">{label}</span>
        {caption && <span className="ss-hint">{caption}</span>}
      </figcaption>
      <image-slot
        id={id}
        shape="rounded"
        radius="14"
        placeholder={t.ui.screenshotPlaceholder}
        {...(fit ? { fit } : {})}
        {...(src ? { src } : {})}
      ></image-slot>
    </figure>
  );
}

/* Pairs stack by default so each image gets the full column width. `split` puts
   them side by side, with `cols` for the unequal split a tall-plus-wide pair needs. */
function ScreenshotPair({ children, split, cols }) {
  return (
    <div
      className={`ss-pair${split ? " ss-pair-split" : ""}`}
      style={cols ? { "--ss-cols": cols } : undefined}
    >
      {children}
    </div>
  );
}

// ---------- Diagram ----------
function ArchitectureDiagram({ t }) {
  const d = t.diagram;
  return (
    <div className="diagram">
      <div className="diagram-title">{d.title}</div>
      <div className="diagram-figure">
      <svg viewBox="0 0 880 256" className="diagram-svg" xmlns="http://www.w3.org/2000/svg">
        <defs>
          <marker id="arrowhead" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">
            <path className="ah" d="M0 0 L10 5 L0 10 z" />
          </marker>
          <marker id="arrowhead-back" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto">
            <path className="ah-back" d="M0 0 L10 5 L0 10 z" />
          </marker>
        </defs>

        {/* Left: the only thing on your own machine */}
        <rect x="16" y="20" width="220" height="192" rx="14" className="zone zone-laptop" />
        <text x="32" y="44" className="zone-label">{d.laptopZone}</text>
        <rect x="36" y="76" width="180" height="80" rx="10" className="node" />
        <text x="126" y="110" className="node-title">{d.browserTitle}</text>
        <text x="126" y="131" className="node-sub">{d.browserSub}</text>

        <line x1="244" y1="116" x2="312" y2="116" className="arrow" markerEnd="url(#arrowhead)" />
        <text x="278" y="105" className="arrow-label">{d.arrow1}</text>

        {/* Middle: the door */}
        <rect x="320" y="76" width="196" height="80" rx="10" className="node node-door" />
        <text x="418" y="108" className="node-title">{d.doorTitle}</text>
        <text x="418" y="129" className="node-sub">{d.doorSub}</text>

        <line x1="524" y1="116" x2="592" y2="116" className="arrow" markerEnd="url(#arrowhead)" />
        <text x="558" y="105" className="arrow-label">{d.arrow2}</text>

        {/* Right: the machine that is theirs */}
        <rect x="600" y="20" width="264" height="192" rx="14" className="zone zone-cloud" />
        <text x="616" y="44" className="zone-label zone-label-mine">{d.cloudZone}</text>
        <rect x="614" y="56" width="236" height="146" rx="10" className="node-frame" />
        <rect x="628" y="68" width="208" height="56" rx="8" className="node node-claude" />
        <text x="732" y="92" className="node-title">{d.claudeTitle}</text>
        <text x="732" y="111" className="node-sub node-sub-claude">{d.claudeSub}</text>
        <rect x="628" y="134" width="208" height="56" rx="8" className="node node-files" />
        <text x="732" y="158" className="node-title">{d.filesTitle}</text>
        <text x="732" y="177" className="node-sub">{d.filesSub}</text>

        {/* The hop the old drawing never showed: the screen comes back */}
        <path d="M 732 212 V 242 H 126 V 214" className="arrow-back" markerEnd="url(#arrowhead-back)" />
        <text x="429" y="234" className="arrow-label arrow-label-back">{d.returnLabel}</text>
      </svg>
      </div>
      <div className="diagram-notes">
        {d.notes.map((n, i) => (
          <div className="diagram-note" key={i}>
            <h4>{n.h}</h4>
            <p>{n.p}</p>
          </div>
        ))}
      </div>
    </div>
  );
}

// ---------- Step block ----------
function StepBlock({ id, n, data, t, heading, screenshotSlot, children }) {
  return (
    <section id={id} className="step" data-screen-label={`${String(n).padStart(2,"0")} ${id}`}>
      <header className="step-head">
        <div className="step-meta">
          <StepNumber n={n} />
          {data.kicker && <span className="kicker">{data.kicker}</span>}
          <span className="step-time">{data.time}</span>
        </div>
        <h2 className="step-title" tabIndex={-1}>{heading || data.title}</h2>
        <p className="step-why">{data.why}</p>
      </header>

      <ol className="step-list">
        {data.items.map((it, i) => (
          <li key={i} className="step-item">
            <span className="step-bullet">{i+1}</span>
            <div className="step-item-body">
              <h4>{it.h}</h4>
              <p>{it.b}</p>
            </div>
          </li>
        ))}
      </ol>

      {children}

      {screenshotSlot}
    </section>
  );
}

// ---------- TOC ----------
function TOC({ t, items, ordered, active, onJump }) {
  const List = ordered ? "ol" : "ul";
  return (
    <nav className="toc" aria-label={t.nav.contents}>
      <div className="toc-label">{t.nav.contents}</div>
      <List className="toc-list">
        {items.map(([id, label]) => (
          <li key={id} className={active === id ? "is-active" : ""}>
            <a href={`#${id}`} onClick={(e)=>{ e.preventDefault(); onJump(id); }}>{label}</a>
          </li>
        ))}
      </List>
    </nav>
  );
}

/* The site's tracks, rendered from GUIDE_I18N.tracks so the app and the static
   pages cannot drift. A pending track links to / with its real destination
   parked in data-pending-href, which is what the fact check compares. */
function TrackNav({ tracks, lang, mode, t }) {
  const here = mode === "tips" ? "/tips" : "/setup";
  return (
    <nav className="tracks" aria-label={t.nav.tracks}>
      <ul>
        {tracks.map((tr) => (
          <li key={tr.href}>
            <a
              href={tr.pending ? "/" : tr.href}
              {...(tr.pending ? { "data-pending-href": tr.href } : {})}
              {...(tr.external ? { "data-external": "true" } : {})}
              {...(tr.href === here ? { "aria-current": "page" } : {})}
            >
              <span>{tr.label[lang] || tr.label.en}</span>
              {tr.external && (
                <>
                  <span className="ext" aria-hidden="true">&#8599;</span>
                  <span className="visually-hidden">{t.nav.externalNote}</span>
                </>
              )}
            </a>
          </li>
        ))}
      </ul>
    </nav>
  );
}

// ---------- Header ----------
function Header({ lang, setLang, t, mode }) {
  return (
    <header className="page-head">
      <div className="page-head-inner">
        <div className="brand">
          <div className="brand-mark" aria-hidden="true">
            <svg viewBox="0 0 24 24" width="22" height="22"><path fill="currentColor" d="M9.5 6.5L4 12l5.5 5.5 1.4-1.4L6.8 12l4.1-4.1L9.5 6.5zm5 11l5.5-5.5L14.5 6.5l-1.4 1.4 4.1 4.1-4.1 4.1 1.4 1.4z"/></svg>
          </div>
          <div className="brand-text">
            <div className="brand-name">Milton Coder</div>
            <div className="brand-sub">{mode === "tips" ? t.tipsHead.eyebrow : t.hero.eyebrow}</div>
          </div>
        </div>
        <div className="head-right">
          <LangToggle lang={lang} setLang={setLang} t={t} />
        </div>
      </div>
      <div className="page-head-tracks">
        <TrackNav tracks={window.GUIDE_I18N.tracks} lang={lang} mode={mode} t={t} />
      </div>
    </header>
  );
}

function LangToggle({ lang, setLang, t }) {
  const opts = [
    { id: "en",   label: "EN",   sub: "English" },
    { id: "zhCN", label: "简体", sub: "简体中文" },
    { id: "zhTW", label: "繁體", sub: "繁體中文" },
  ];
  return (
    <div className="lang" role="radiogroup" aria-label={t.ui.languageLabel}>
      <span className="lang-label">{t.ui.languageLabel}</span>
      <div className="lang-segments">
        {opts.map(o => (
          <button
            key={o.id}
            role="radio"
            aria-checked={lang === o.id}
            className={`lang-seg ${lang === o.id ? "is-on" : ""}`}
            onClick={() => setLang(o.id)}
            title={o.sub}
          >
            <span className="lang-seg-label">{o.label}</span>
          </button>
        ))}
      </div>
    </div>
  );
}

// ---------- Hero ----------
function Hero({ t, items }) {
  return (
    <section className="hero" id="overview" data-screen-label="00 overview">
      <div className="hero-head">
        <div className="hero-meta">
          <span className="badge">{t.hero.eyebrow}</span>
          <span className="hero-time">⏱ {t.ui.time}</span>
          <span className="hero-aud">· {t.ui.audience}</span>
        </div>
        <h1 className="hero-title">{t.hero.title}</h1>
        <p className="hero-lede">{t.hero.lede}</p>
      </div>

      <div className="hero-lower">
        <div className="hero-progress">
          {items.map(([id, label], i) => (
            <a key={id} href={`#${id}`} className="hero-pill">
              <span className="hero-pill-n">{String(i+1).padStart(2,"0")}</span>
              <span className="hero-pill-l">{label}</span>
            </a>
          ))}
        </div>

        <div className="hero-art">
          <ArchitectureDiagram t={t} />
        </div>
      </div>
    </section>
  );
}

function TipsHead({ t }) {
  const d = t.tipsHead;
  return (
    <section className="hero hero-tips">
      <div className="hero-head">
        <div className="hero-meta">
          <span className="badge">{d.eyebrow}</span>
        </div>
        <h1 className="hero-title">{d.title}</h1>
        <p className="hero-lede">{d.lede}</p>
      </div>
    </section>
  );
}

// ─────────────────────────────────────────────────────────────
// HOW WE BUILD: Tools / Saving / GitHub / Workflow / Quality / Rules / Claude rules
// ─────────────────────────────────────────────────────────────

function SectionHeader({ kicker, title, lede }) {
  return (
    <div className="block-head">
      <Kicker>{kicker}</Kicker>
      <h2 tabIndex={-1}>{title}</h2>
      {lede && <p className="block-lede">{lede}</p>}
    </div>
  );
}

function ToolsBlock({ t }) {
  const d = t.tools;
  return (
    <section id="tools" className="block hiw-block" data-screen-label="11 tools">
      <SectionHeader kicker={d.kicker} title={d.title} lede={d.lede} />

      <div className="tools-groups">
        {d.groups.map((g, gi) => (
          <div key={gi} className="tools-group">
            <div className="tools-group-name">{g.name}</div>
            <div className="tools-grid">
              {g.items.map((it, i) => (
                <div key={i} className="tool-card">
                  <div className="tool-glyph" aria-hidden="true">{it.glyph}</div>
                  <div className="tool-meta">
                    <div className="tool-name">{it.name}</div>
                    <div className="tool-role">{it.role}</div>
                  </div>
                  <div className="tool-desc">{rich(it.desc)}</div>
                </div>
              ))}
            </div>
          </div>
        ))}
      </div>

      <div className="claude-ways">
        <h3 className="ways-title">{d.claudeWaysTitle}</h3>
        <div className="claude-ways-grid">
          {d.claudeWays.map((w, i) => (
            <div key={i} className="claude-way">
              <div className="claude-way-glyph" aria-hidden="true">{w.glyph}</div>
              <div className="claude-way-name">{w.name}</div>
              <div className="claude-way-desc">{w.desc}</div>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

function SavingBlock({ t }) {
  const d = t.saving;
  return (
    <section id="saving" className="block hiw-block" data-screen-label="12 saving">
      <SectionHeader kicker={d.kicker} title={d.title} lede={d.lede} />

      <div className="two-kinds">
        {d.twoKinds.map((k, i) => (
          <div key={i} className={`two-kind two-kind-${i}`}>
            <div className="two-kind-tag">{k.tag}</div>
            <div className="two-kind-trigger">{k.trigger}</div>
            <p className="two-kind-desc">{k.desc}</p>
          </div>
        ))}
      </div>

      <div className="analogy-box">
        <div className="analogy-mark">↪</div>
        <p>{d.analogy}</p>
      </div>

      <h3 className="sub-h">{d.flowTitle}</h3>
      <div className="save-flow">
        {d.flow.map((s, i) => (
          <React.Fragment key={i}>
            <div className={`save-flow-node save-flow-node-${i}`}>
              <div className="save-flow-i">{String(i+1).padStart(2,"0")}</div>
              <div className="save-flow-tag">{s.tag}</div>
              <div className="save-flow-note">{s.note}</div>
            </div>
            {i < d.flow.length - 1 && <div className="save-flow-arrow">→</div>}
          </React.Fragment>
        ))}
      </div>

      <Callout kind="warn" title={t.ui.callout_warning}>{d.warning}</Callout>
      <Callout kind="tip"  title={t.ui.callout_tip}>{d.tip}</Callout>
    </section>
  );
}

function GhBlock({ t }) {
  const d = t.gh;
  return (
    <section id="gh" className="block hiw-block" data-screen-label="13 github">
      <SectionHeader kicker={d.kicker} title={d.title} lede={d.lede} />

      <h3 className="sub-h">{d.vsTitle}</h3>
      <div className="vs-row">
        {d.vs.map((v, i) => (
          <React.Fragment key={i}>
            <div className={`vs-card vs-card-${i}`}>
              <div className="vs-card-tag">{v.tag}</div>
              <p>{v.desc}</p>
            </div>
            {i === 0 && <div className="vs-divider"><span>{d.vsDivider}</span></div>}
          </React.Fragment>
        ))}
      </div>

      <h3 className="sub-h">{d.branchesTitle}</h3>
      <div className="branches-card">
        <svg viewBox="0 0 700 200" xmlns="http://www.w3.org/2000/svg" className="branches-svg" preserveAspectRatio="xMidYMid meet">
          {/* main — the only long-lived branch, and production */}
          <line x1="40" y1="70" x2="660" y2="70" className="branch-main"/>
          <text x="40" y="52" className="branch-label main">main</text>
          <text x="40" y="40" className="branch-sub" style={{fontSize:"9px"}}>{d.diagramProduction}</text>
          <circle cx="100" cy="70" r="5" className="branch-dot main"/>
          <circle cx="200" cy="70" r="5" className="branch-dot main"/>
          <circle cx="560" cy="70" r="7" className="branch-dot main merge"/>
          <text x="560" y="46" className="branch-sub" textAnchor="middle">{d.diagramMergeLive}</text>

          {/* your branch — off main, back into main through one PR */}
          <path d="M200 70 Q260 150 320 150" fill="none" className="branch-feature"/>
          <line x1="320" y1="150" x2="500" y2="150" className="branch-feature"/>
          <path d="M500 150 Q540 120 560 70" fill="none" className="branch-feature"/>
          <circle cx="370" cy="150" r="5" className="branch-dot feature"/>
          <circle cx="440" cy="150" r="5" className="branch-dot feature"/>
          <text x="410" y="172" textAnchor="middle" className="branch-label feature">your/MILTON-123-thing</text>
          <text x="574" y="118" className="branch-sub">PR → main</text>
        </svg>
      </div>
      <div className="analogy-box">
        <div className="analogy-mark">⌥</div>
        <p>{d.branchesAnalogy}</p>
      </div>

      {d.flow3dTitle && (
        <a className="flow3d-card" href="/gitflow/">
          <div className="flow3d-glyph" aria-hidden="true">◆</div>
          <div className="flow3d-body">
            <div className="flow3d-title">{d.flow3dTitle}</div>
            <p className="flow3d-desc">{d.flow3dDesc}</p>
          </div>
          <span className="flow3d-cta">{d.flow3dCta}</span>
        </a>
      )}

      {d.ruleTitle && (
        <>
          <h3 className="sub-h">{d.ruleTitle}</h3>
          <Callout kind="warn" title="">{d.rule}</Callout>
        </>
      )}
    </section>
  );
}

function WorkflowBlock({ t }) {
  const d = t.workflow;
  let stepIdx = 0;
  return (
    <section id="workflow" className="block hiw-block" data-screen-label="14 workflow">
      <SectionHeader kicker={d.kicker} title={d.title} lede={d.lede} />

      <div className="wf-pipeline">
        {d.groups.map((g, gi) => (
          <div key={gi} className={`wf-group wf-group-${g.color}`}>
            <div className="wf-group-rail" aria-hidden="true">
              <div className="wf-group-label">{g.label}</div>
            </div>
            <div className="wf-steps">
              {g.steps.map((s, si) => {
                stepIdx += 1;
                return (
                  <div key={si} className="wf-step">
                    <div className="wf-step-n">{String(stepIdx).padStart(2,"0")}</div>
                    <div className="wf-step-body">
                      <h4>{s.h}</h4>
                      <p>{s.b}</p>
                    </div>
                  </div>
                );
              })}
            </div>
          </div>
        ))}
      </div>

      <div className="wf-strip">
        <div className="wf-strip-title">{d.stripTitle}</div>
        <div className="wf-strip-rail">
          {d.strip.map((s, i) => (
            <React.Fragment key={i}>
              <div className="wf-strip-cell">{s}</div>
              {i < d.strip.length - 1 && <div className="wf-strip-arrow">→</div>}
            </React.Fragment>
          ))}
        </div>
      </div>
    </section>
  );
}

function QualityBlock({ t }) {
  const d = t.quality;
  return (
    <section id="quality" className="block hiw-block" data-screen-label="15 quality">
      <SectionHeader kicker={d.kicker} title={d.title} lede={d.lede} />

      {d.meiCardTitle && (
        <a className="mei-card" href="/mei/">
          <div className="mei-glyph" aria-hidden="true">◆</div>
          <div className="mei-body">
            <div className="mei-title">{d.meiCardTitle}</div>
            <p className="mei-desc">{d.meiCardDesc}</p>
          </div>
          <span className="mei-cta">{d.meiCardCta}</span>
        </a>
      )}

      <h3 className="sub-h">{d.whenTitle}</h3>
      <table className="tbl">
        <thead>
          <tr>
            <th>{d.whenHeading[0]}</th>
            <th>{d.whenHeading[1]}</th>
          </tr>
        </thead>
        <tbody>
          {d.whenRows.map((r, i) => (
            <tr key={i}>
              <td>{r[0]}</td>
              <td>{r[1]}</td>
            </tr>
          ))}
        </tbody>
      </table>

      <h3 className="sub-h">{d.askTitle}</h3>
      <CmdBlock lines={d.askLines} t={t} />

      <Callout kind="tip" title={t.ui.callout_tip}>{d.tip}</Callout>
    </section>
  );
}

function RulesBlock({ t }) {
  const d = t.rules;
  return (
    <section id="rules" className="block hiw-block" data-screen-label="16 rules">
      <SectionHeader kicker={d.kicker} title={d.title} lede={d.lede} />
      <div className="rules-grid">
        {d.items.map((it, i) => (
          <div key={i} className={`rule rule-${it.accent}`}>
            <div className="rule-glyph">{it.glyph}</div>
            <div className="rule-body">
              <h4>{it.h}</h4>
              <p>{rich(it.b)}</p>
            </div>
          </div>
        ))}
      </div>
    </section>
  );
}

function CmdBlock({ lines, t }) {
  // accept arbitrary lines: lines starting with "#" are comments,
  // lines starting with ">" are interactive prompts, others are shell.
  return (
    <pre className="cmd cmd-rich" aria-label="terminal command">
      {lines.map((l, i) => {
        const isComment = l.trimStart().startsWith("#");
        const isPrompt  = l.trimStart().startsWith(">");
        return (
          <span key={i} className={`cmd-line ${isComment ? "is-comment" : ""} ${isPrompt ? "is-prompt" : ""}`}>
            <span className="cmd-gutter">{String(i+1).padStart(2,"0")}</span>
            <span className="cmd-prompt">
              {isComment ? "#" : isPrompt ? ">" : "$"}
            </span>
            <span className="cmd-text">{isComment ? l.replace(/^\s*#\s?/, "") : isPrompt ? l.replace(/^\s*>\s?/, "") : l}</span>
          </span>
        );
      })}
    </pre>
  );
}

function CheatsheetBlock({ t }) {
  const d = t.cheatsheet;
  return (
    <section id="cheatsheet" className="block hiw-block" data-screen-label="17 cheatsheet">
      <SectionHeader kicker={d.kicker} title={d.title} lede={d.lede} />
      <div className="cheats">
        {d.groups.map((g, i) => (
          <div key={i} className="cheat">
            <div className="cheat-name">{g.name}</div>
            <CmdBlock lines={g.lines} t={t} />
          </div>
        ))}
      </div>
    </section>
  );
}

function SetupBlock({ t }) {
  const d = t.setup;
  return (
    <section id="setup" className="block hiw-block" data-screen-label="18 setup">
      <SectionHeader kicker={d.kicker} title={d.title} lede={d.lede} />

      <div className="analogy-box">
        <div className="analogy-mark">≡</div>
        <p>{d.analogy}</p>
      </div>

      <Callout kind="info" title={t.ui.callout_workspace}>{d.coderNote}</Callout>

      <h3 className="sub-h">{d.twoTypesTitle}</h3>
      <div className="rule-types">
        {d.twoTypes.map((r, i) => (
          <div key={i} className={`rule-type rule-type-${i}`}>
            <div className="rule-type-tag">{r.tag}</div>
            <code className="rule-type-path">{r.path}</code>
            <p>{r.desc}</p>
          </div>
        ))}
      </div>

      <h3 className="sub-h">{d.addTitle}</h3>
      <p className="block-lede">{d.addLede}</p>
      <CmdBlock lines={d.addCmd} t={t} />
      <Callout kind="tip" title={t.ui.callout_tip}>{d.addTip}</Callout>

      <h3 className="sub-h">{d.doesTitle}</h3>
      <div className="does-grid">
        {d.does.map((it, i) => (
          <div key={i} className="does">
            <div className="does-tag">{it.tag}</div>
            <p>{it.b}</p>
          </div>
        ))}
      </div>
    </section>
  );
}

// ---------- Daily / stop / trouble / donts ----------
function DailyBlock({ t }) {
  const d = t.daily;
  return (
    <section id="daily" className="block" data-screen-label="06 daily">
      <div className="block-head">
        <Kicker>{d.kicker}</Kicker>
        <h2 tabIndex={-1}>{d.title}</h2>
        <p className="block-lede">{d.lede}</p>
      </div>
      <div className="ways">
        {d.ways.map((w, i) => (
          <div key={i} className="way">
            <div className="way-num">{String(i+1).padStart(2,"0")}</div>
            <div className="way-name">{w.name}</div>
            <div className="way-role">{w.role}</div>
            <div className="way-desc">{rich(w.desc)}</div>
          </div>
        ))}
      </div>
    </section>
  );
}

function GitIdentityBlock({ t, heading }) {
  const d = t.gitidentity;
  return (
    <section id="gitidentity" className="block" data-screen-label="10 gitidentity">
      <SectionHeader kicker={d.kicker} title={heading || d.title} lede={d.lede} />
      <Cmd t={t} lines={[
        'git config --global user.name "Your Name"',
        'git config --global user.email "you@milton.co"',
      ]} />
    </section>
  );
}

function LaptopBlock({ t, heading }) {
  const d = t.laptop;
  if (!d) return null;
  return (
    <section id="laptop" className="block hiw-block" data-screen-label="11 laptop">
      <SectionHeader kicker={d.kicker} title={heading || d.title} lede={d.lede} />
      <div className="tools-grid">
        {d.items.map((it, i) => {
          const offsite = /^https?:/.test(it.href);
          return (
            <a key={i} className="tool-card tool-card-link" href={it.href}
               {...(offsite ? { target: "_blank", rel: "noopener noreferrer" } : {})}>
              <div className="tool-glyph" aria-hidden="true">{it.glyph}</div>
              <div className="tool-meta">
                <div className="tool-name">{it.name}</div>
                <div className="tool-role">{it.role}</div>
              </div>
              <div className="tool-desc">{it.desc}</div>
            </a>
          );
        })}
      </div>
    </section>
  );
}

function StopBlock({ t }) {
  const s = t.stop;
  return (
    <section id="stop" className="block" data-screen-label="07 stop">
      <div className="block-head">
        <Kicker>{s.kicker}</Kicker>
        <h2 tabIndex={-1}>{s.title}</h2>
        <p className="block-lede">{s.lede}</p>
      </div>
      <div className="stop-flow">
        {s.flowLabels.map((l, i) => (
          <React.Fragment key={i}>
            <div className={`stop-node ${i === 0 ? "is-running" : ""} ${i === 2 ? "is-stopped" : ""} ${i === 4 ? "is-resumed" : ""}`}>
              <span className="stop-node-i">{String(i+1).padStart(2,"0")}</span>
              <span className="stop-node-l">{l}</span>
            </div>
            {i < s.flowLabels.length - 1 && <div className="stop-arrow">→</div>}
          </React.Fragment>
        ))}
      </div>
      <ul className="bullets">
        {s.bullets.map((b, i) => <li key={i}>{rich(b)}</li>)}
      </ul>
    </section>
  );
}

function TroubleBlock({ t }) {
  const tb = t.trouble;
  return (
    <section id="trouble" className="block" data-screen-label="08 trouble">
      <div className="block-head">
        <Kicker>{tb.kicker}</Kicker>
        <h2 tabIndex={-1}>{tb.title}</h2>
      </div>
      <table className="tbl">
        <thead>
          <tr>
            <th>{t.ui.tableHeading_symptom}</th>
            <th>{t.ui.tableHeading_fix}</th>
          </tr>
        </thead>
        <tbody>
          {tb.rows.map((r, i) => (
            <tr key={i}>
              <td>{r[0]}</td>
              <td>{rich(r[1])}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </section>
  );
}

function DontsBlock({ t }) {
  const d = t.donts;
  return (
    <section id="donts" className="block" data-screen-label="09 donts">
      <div className="block-head">
        <Kicker>{d.kicker}</Kicker>
        <h2 tabIndex={-1}>{d.title}</h2>
      </div>
      <div className="donts">
        {d.items.map((it, i) => (
          <div key={i} className="dont">
            <div className="dont-x">✕</div>
            <div className="dont-body">
              <h4>{it.h}</h4>
              <p>{rich(it.b)}</p>
            </div>
          </div>
        ))}
      </div>
    </section>
  );
}

function ConnectorsBlock({ t }) {
  const d = t.connectors;
  if (!d) return null;
  return (
    <section id="connectors" className="block hiw-block" data-screen-label="19 connectors">
      <SectionHeader kicker={d.kicker} title={d.title} lede={d.lede} />
      <div className="tools-grid">
        {d.items.map((it, i) => (
          <a key={i} className="tool-card tool-card-link" href={it.href} aria-label={it.name}>
            <div className="tool-glyph" aria-hidden="true">{it.glyph}</div>
            <div className="tool-meta">
              <div className="tool-name">{it.name}</div>
              <div className="tool-role">{it.role}</div>
            </div>
            <div className="tool-desc">{it.desc}</div>
          </a>
        ))}
      </div>
    </section>
  );
}


// ---------- App ----------

/* In setup mode a block's own title is prefixed with its position, as heading
   TEXT: a badge alone leaves a screen reader with no idea how far in it is.
   A section with no title of its own falls back to its table-of-contents label
   rather than throwing the page down. */
const setupTitle = (t, id, fallback) => {
  const s = t.steps[id] || t[id];
  return (s && s.title) || fallback || id;
};

function App() {
  /* The entry page declares the mode. A tree that carries only one mode's
     sections falls back to that one, so a tips-only tree cannot take the setup
     branch and read a `hero` it does not have. */
  const declared = window.GUIDE_MODE === "tips" ? "tips" : "setup";
  const present = window.GUIDE_I18N.sections.map(s => s.mode);
  const mode = present.indexOf(declared) === -1 ? (present[0] || declared) : declared;

  // Reading or writing storage throws outright in some contexts, which would
  // take the whole page down over a remembered language.
  const [lang, setLang] = useState(() => {
    try { return localStorage.getItem("guide-lang") || "en"; } catch (e) { return "en"; }
  });
  useEffect(() => {
    try { localStorage.setItem("guide-lang", lang); } catch (e) {}
  }, [lang]);

  /* Every non-structural key in GUIDE_I18N resolves to the reader's language,
     with English as the fallback, and `steps` the same way one level down. The
     runtime names no key: a new section needs a block and a string, not an edit
     here, and a missing key is null rather than a throw. */
  const t = useMemo(() => {
    const STRUCTURAL = ["sections", "tracks", "sectionLabels", "steps"];
    const pick = (obj) => (obj ? (obj[lang] || obj.en || null) : null);
    const out = {};
    Object.keys(window.GUIDE_I18N).forEach((k) => {
      if (STRUCTURAL.indexOf(k) !== -1) return;
      out[k] = pick(window.GUIDE_I18N[k]);
    });
    const steps = window.GUIDE_I18N.steps || {};
    out.steps = {};
    Object.keys(steps).forEach((k) => { out.steps[k] = pick(steps[k]); });
    return out;
  }, [lang]);

  const ids = useMemo(
    () => window.GUIDE_I18N.sections.filter(s => s.mode === mode).map(s => s.id),
    [mode]
  );

  const toc = useMemo(() => ids.map((id) => {
    const l = window.GUIDE_I18N.sectionLabels[id];
    return [id, (l && (l[lang] || l.en)) || id];
  }), [ids, lang]);

  useEffect(() => {
    document.title = t.meta.title[mode];
    document.documentElement.lang = t.meta.lang;
  }, [t, mode]);

  // Active section tracker via IntersectionObserver
  const [active, setActive] = useState(ids[0]);
  useEffect(() => {
    const els = ids.map(id => document.getElementById(id)).filter(Boolean);
    if (!els.length) return;
    /* One callback can carry several records for the same section: a smooth
       scroll through a short section produces its enter and its leave in one
       frame. Filtering records by isIntersecting keeps the stale enter, and
       being higher on the page it wins the sort. So the set of sections in the
       band is kept across callbacks, last record per target winning, and the
       active one is the topmost of that set. */
    const inBand = new Set();
    const obs = new IntersectionObserver((entries) => {
      entries.forEach(e => { if (e.isIntersecting) inBand.add(e.target); else inBand.delete(e.target); });
      const visible = Array.from(inBand)
        .sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
      if (visible[0]) setActive(visible[0].id);
    }, { rootMargin: "-25% 0px -55% 0px", threshold: [0, 0.2, 0.6] });
    els.forEach(el => obs.observe(el));
    return () => obs.disconnect();
  }, [ids, t]);

  /* Scrolling alone leaves a keyboard or screen-reader user where they were, so
     the heading takes focus too. It is not in the tab order, hence the -1. */
  const goTo = (id) => {
    const el = document.getElementById(id);
    if (!el) return;
    const reduce = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    // Measured, not a constant: the sticky header wraps on a narrow viewport.
    const head = document.querySelector(".page-head");
    const offset = (head ? head.offsetHeight : 92) + 12;
    const top = el.getBoundingClientRect().top + window.scrollY - offset;
    window.scrollTo({ top: Math.max(0, top), behavior: reduce ? "auto" : "smooth" });
    const heading = el.querySelector("h2");
    if (heading) heading.focus({ preventScroll: true });
  };

  // A fragment aimed at this mode is honoured on load; one aimed at the other
  // mode never arrives, because the hub forwards it before this page loads.
  useEffect(() => {
    let hash = "";
    try { hash = decodeURIComponent((window.location.hash || "").slice(1)); } catch (e) { hash = ""; }
    if (!hash || ids.indexOf(hash) === -1) return;
    const raf = window.requestAnimationFrame(() => goTo(hash));
    return () => window.cancelAnimationFrame(raf);
  }, [ids]);

  const total = ids.length;

  return (
    <>
      <Header lang={lang} setLang={setLang} t={t} mode={mode} />

      <main id="main" tabIndex={-1}>
        {mode === "setup" ? <Hero t={t} items={toc} /> : <TipsHead t={t} />}

        <div className="page-grid">
          <aside className="sidebar">
            <TOC t={t} items={toc} ordered={mode === "setup"} active={active} onJump={goTo} />
          </aside>

          <div className="main">
            {ids.map((id, i) => {
              const render = (window.GUIDE_BLOCKS || {})[id];
              if (!render) return null;
              const heading = mode === "setup"
                ? `${t.ui.stepOf.replace("{n}", i + 1).replace("{total}", total)}: ${setupTitle(t, id, toc[i] && toc[i][1])}`
                : null;
              return (
                <React.Fragment key={id}>
                  {render({ t, n: i + 1, heading })}
                </React.Fragment>
              );
            })}
          </div>
        </div>
      </main>
    </>
  );
}
