// iPhone onboarding — fully interactive 8-step funnel.
// Shared chrome + step bodies. All controls wired to parent state via props.

const M = window.MS_TOKENS;
const { useState, useEffect, useRef } = React;

// -----------------------------------------------------------------------------
// Shared libraries (exported so App can derive defaults/labels)
// -----------------------------------------------------------------------------
const TOPICS_LIBRARY = [
  "AI", "Longevity", "Health", "Geopolitics", "Research", "Nutrition",
  "Exercise", "Markets", "Biotech", "Climate", "Space", "Policy",
];

const SOURCES_LIBRARY = [
  { id: "ft",      name: "Financial Times", mark: "FT",  bg: "#FFF1E5", fg: "#000" },
  { id: "econ",    name: "The Economist",   mark: "TE",  bg: "#E3120B", fg: "#fff" },
  { id: "nature",  name: "Nature",          mark: "N",   bg: "#006633", fg: "#fff" },
  { id: "wiki",    name: "Wikipedia",       mark: "W",   bg: "#fff",    fg: "#111" },
  { id: "arxiv",   name: "arXiv",           mark: "aX",  bg: "#b31b1b", fg: "#fff" },
  { id: "reuters", name: "Reuters",         mark: "R",   bg: "#FA6400", fg: "#fff" },
  { id: "nejm",    name: "NEJM",            mark: "NE",  bg: "#cc0000", fg: "#fff" },
  { id: "bloomb",  name: "Bloomberg",       mark: "B",   bg: "#000",    fg: "#fff" },
  { id: "mittr",   name: "MIT TR",          mark: "MIT", bg: "#A41F35", fg: "#fff" },
  { id: "guard",   name: "Guardian",        mark: "G",   bg: "#052962", fg: "#fff" },
  { id: "strat",   name: "Stratechery",     mark: "ST",  bg: "#1a1a1a", fg: "#fff" },
  { id: "semafor", name: "Semafor",         mark: "Sm",  bg: "#1f1f1f", fg: "#fff" },
];

const DEFAULT_SOURCE_IDS = ["ft", "econ", "nature", "wiki", "reuters", "mittr"];

const STYLES_LIBRARY = [
  { id: "original",  name: "Original",  italic: "Signal" },
  { id: "editorial", name: "Editorial" },
  { id: "developer", name: "Developer" },
  { id: "research",  name: "Research" },
  { id: "custom",    name: "Custom",    locked: true },
];

const HOURS = [4, 5, 6, 7, 8, 9, 10];
const MINUTES = [0, 15, 30, 45];
const DAY_LABELS = ["M", "T", "W", "T", "F", "S", "S"];
const DAY_NAMES_FULL = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
const MONTH_ABBR = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];

const pad2 = (n) => String(n).padStart(2, "0");

// -----------------------------------------------------------------------------
// Shared chrome
// -----------------------------------------------------------------------------
function MScreen({ step, total = 8, children, scroll = true, dark = false }) {
  const bg = dark ? M.ink : M.paper;
  const fg = dark ? M.paper : M.ink;
  return (
    <div style={{
      width: "100%", height: "100%", background: bg, color: fg,
      fontFamily: M.body, display: "flex", flexDirection: "column",
      position: "relative", overflow: "hidden",
    }}>
      {!dark && <div style={{
        position: "absolute", inset: 0, pointerEvents: "none", opacity: 0.5,
        backgroundImage: `radial-gradient(rgba(90,70,30,0.055) 1px, transparent 1px)`,
        backgroundSize: "3px 3px",
      }}/>}
      <div style={{
        padding: "calc(14px + env(safe-area-inset-top)) 20px 10px",
        borderBottom: `1px solid ${dark ? "#2a2f3b" : M.rule2}`,
        display: "flex", alignItems: "center", justifyContent: "space-between",
        background: bg, position: "relative", zIndex: 1, flexShrink: 0,
      }}>
        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
          <svg width="22" height="22" viewBox="0 0 40 40">
            <circle cx="20" cy="20" r="18" fill="none" stroke={fg} strokeWidth="1" opacity="0.35"/>
            <circle cx="20" cy="20" r="12" fill="none" stroke={fg} strokeWidth="1" opacity="0.6"/>
            <circle cx="20" cy="20" r="6" fill="none" stroke={fg} strokeWidth="1.2"/>
            <circle cx="20" cy="20" r="2.2" fill={M.amber}/>
          </svg>
          <div style={{ fontFamily: M.display, fontSize: 16, fontWeight: 700, letterSpacing: -0.5 }}>
            Millennium <span style={{ fontStyle: "italic", color: dark ? M.amber : M.navy }}>Signal</span>
          </div>
        </div>
        <div style={{ fontFamily: M.mono, fontSize: 9, letterSpacing: 1.8, color: dark ? "#a29b87" : M.mid, textTransform: "uppercase" }}>
          {pad2(step)} / {pad2(total)}
        </div>
      </div>
      <div style={{ display: "flex", gap: 3, padding: "8px 20px", background: bg, position: "relative", zIndex: 1, flexShrink: 0 }}>
        {Array.from({ length: total }).map((_, i) => (
          <div key={i} style={{
            flex: 1, height: 2,
            background: i < step - 1 ? fg : (i === step - 1 ? M.amber : (dark ? "#2a2f3b" : M.rule2)),
          }}/>
        ))}
      </div>
      <div style={{ flex: 1, minHeight: 0, overflow: scroll ? "auto" : "hidden", position: "relative", zIndex: 1 }}>
        {children}
      </div>
    </div>
  );
}

function MKicker({ num, children }) {
  return (
    <div style={{ display: "flex", alignItems: "baseline", gap: 8 }}>
      {num && <span style={{ fontFamily: M.display, fontStyle: "italic", fontWeight: 700, fontSize: 13, color: M.red }}>{num}</span>}
      <span style={{ fontFamily: M.mono, fontSize: 9.5, letterSpacing: 2.5, color: M.red, fontWeight: 700, textTransform: "uppercase" }}>{children}</span>
    </div>
  );
}

function MHed({ children, italic, size = 32 }) {
  return (
    <h1 style={{ fontFamily: M.display, fontSize: size, fontWeight: 700, letterSpacing: -1.2, lineHeight: 1.03, margin: "10px 0 0" }}>
      {children}{italic && <span style={{ fontStyle: "italic", color: M.navy }}> {italic}</span>}
    </h1>
  );
}

function MDeck({ children }) {
  return <p style={{ fontFamily: M.display, fontStyle: "italic", fontSize: 14, color: "#3a362c", lineHeight: 1.4, margin: "8px 0 0" }}>{children}</p>;
}

function MBtn({ children, primary = true, arrow = true, amber, onClick, disabled }) {
  const bg = disabled ? M.rule2 : (amber ? M.amber : (primary ? M.ink : "transparent"));
  const fg = disabled ? M.mid : (amber ? M.ink : (primary ? M.paper : M.ink));
  return (
    <button onClick={onClick} disabled={disabled} style={{
      width: "100%", background: bg, color: fg,
      border: primary || amber ? "none" : `1px solid ${M.ink}`,
      padding: "14px 18px", fontFamily: M.display, fontSize: 16, fontWeight: 700,
      letterSpacing: -0.2, display: "flex", justifyContent: "space-between", alignItems: "center",
      cursor: disabled ? "not-allowed" : "pointer", borderRadius: 2,
    }}>
      <span>{children}</span>
      {arrow && <span style={{ fontStyle: "italic", color: amber ? M.ink : M.amber, fontSize: 18 }}>→</span>}
    </button>
  );
}

// =============================================================================
// 01 · Entry — collect email + choose Default or Custom
// =============================================================================
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

function MStep01({ email, onSetEmail, onPick }) {
  const valid = EMAIL_RE.test(email || "");
  const tryPick = (mode) => {
    if (!valid) return;
    onPick(mode);
  };
  return (
    <MScreen step={1}>
      <div style={{ padding: "22px 20px 10px" }}>
        <MKicker num="I.">How we begin</MKicker>
        <MHed italic="address.">Where to send your</MHed>
        <MDeck>Your inbox is your account. Change anything later by reply.</MDeck>
      </div>

      <div style={{
        margin: "16px 20px 0", border: `1.5px solid ${valid ? M.ink : M.amber}`,
        background: M.paper2, padding: "16px 16px 14px",
        animation: valid ? "none" : "ms-pulse-ring 2.4s ease-out infinite",
      }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
          <span style={{ fontFamily: M.mono, fontSize: 9.5, letterSpacing: 2.5, color: M.red, fontWeight: 700, textTransform: "uppercase" }}>
            ▸ Step one · your email
          </span>
          <span style={{ fontFamily: M.mono, fontSize: 8.5, letterSpacing: 1.5, color: valid ? M.amber : M.mid, fontWeight: 700 }}>
            {valid ? "✓ READY" : "REQUIRED"}
          </span>
        </div>
        <input
          type="email"
          inputMode="email"
          autoComplete="email"
          autoCapitalize="off"
          autoCorrect="off"
          spellCheck={false}
          autoFocus
          value={email || ""}
          onChange={e => onSetEmail(e.target.value)}
          placeholder="you@example.com"
          style={{
            width: "100%", marginTop: 10, padding: "10px 0",
            fontFamily: M.display, fontSize: 24, letterSpacing: -0.5,
            border: "none", borderBottom: `2px solid ${valid ? M.ink : M.amber}`,
            background: "transparent", outline: "none", color: M.ink,
            transition: "border-color 200ms",
          }}
        />
        <div style={{ marginTop: 8, fontFamily: M.display, fontStyle: "italic", fontSize: 12.5, color: "#3a362c", lineHeight: 1.4 }}>
          {valid
            ? "Looks good. Pick a path below — we'll send your first edition to this address."
            : "We'll send your full briefing here. Reply to any edition to change anything."}
        </div>
      </div>

      <div style={{ padding: "18px 20px 6px", fontFamily: M.mono, fontSize: 9, letterSpacing: 2.5, color: M.red, fontWeight: 700, textTransform: "uppercase" }}>
        — then choose how —
      </div>

      <div
        onClick={() => tryPick("default")}
        style={{
          margin: "8px 20px 0", border: `1px solid ${M.rule}`, background: M.paper2,
          padding: 18, cursor: valid ? "pointer" : "not-allowed", opacity: valid ? 1 : 0.5,
        }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
          <span style={{ fontFamily: M.mono, fontSize: 9.5, letterSpacing: 2.5, color: M.red, fontWeight: 700 }}>RECOMMENDED</span>
          <span style={{ fontFamily: M.script, border: `1.5px solid ${M.red}`, color: M.red, padding: "3px 8px", fontSize: 10, fontWeight: 700, transform: "rotate(-3deg)", lineHeight: 1 }}>60 SEC</span>
        </div>
        <div style={{ fontFamily: M.display, fontSize: 32, fontWeight: 700, letterSpacing: -1, lineHeight: 1, marginTop: 10 }}>
          The <span style={{ fontStyle: "italic", color: M.navy }}>Default</span>
        </div>
        <div style={{ fontFamily: M.display, fontStyle: "italic", fontSize: 13, color: "#3a362c", marginTop: 6, lineHeight: 1.4 }}>
          Our editor's curation. Daily at 07:00.
        </div>
        <div style={{ borderTop: `1px dashed ${M.rule}`, marginTop: 12, paddingTop: 10, fontFamily: M.mono, fontSize: 9.5, letterSpacing: 1.2, color: M.mid, lineHeight: 1.7 }}>
          ▸ 6 trusted sources<br/>▸ Original style<br/>▸ Editable any time
        </div>
        <div style={{ marginTop: 14 }}>
          <MBtn disabled={!valid} onClick={(e) => { e.stopPropagation(); tryPick("default"); }}>Use the Default</MBtn>
        </div>
      </div>
      <div
        onClick={() => tryPick("custom")}
        style={{
          margin: "12px 20px 24px", border: `1px solid ${M.ink}`, background: M.ink, color: M.paper,
          padding: 18, cursor: valid ? "pointer" : "not-allowed", opacity: valid ? 1 : 0.55,
        }}>
        <span style={{ fontFamily: M.mono, fontSize: 9.5, letterSpacing: 2.5, color: M.amber, fontWeight: 700 }}>~ 3 MIN</span>
        <div style={{ fontFamily: M.display, fontSize: 32, fontWeight: 700, letterSpacing: -1, lineHeight: 1, marginTop: 10 }}>
          <span style={{ fontStyle: "italic", color: M.amber }}>Custom</span>
        </div>
        <div style={{ fontFamily: M.display, fontStyle: "italic", fontSize: 13, color: "#d9d2bf", marginTop: 6, lineHeight: 1.4 }}>
          Shape it yourself — topics, sources, style, cadence.
        </div>
        <button
          disabled={!valid}
          onClick={(e) => { e.stopPropagation(); tryPick("custom"); }}
          style={{
            marginTop: 14, width: "100%", background: "transparent", color: M.paper,
            border: `1px solid ${M.paper}`, padding: "14px 18px",
            fontFamily: M.display, fontSize: 16, fontWeight: 700,
            cursor: valid ? "pointer" : "not-allowed",
            display: "flex", justifyContent: "space-between", alignItems: "center",
          }}>
          <span>Start Custom</span>
          <span style={{ color: M.amber, fontStyle: "italic", fontSize: 18 }}>→</span>
        </button>
      </div>
    </MScreen>
  );
}

// =============================================================================
// 02 · Topics — text input + voice + library chips
// =============================================================================
function MStep02({ topics, onAddTopic, onRemoveTopic }) {
  const [draft, setDraft] = useState("");
  const [recording, setRecording] = useState(false);
  const recogRef = useRef(null);
  const inputRef = useRef(null);

  const commit = () => {
    const t = draft.trim();
    if (t && !topics.includes(t)) onAddTopic(t);
    setDraft("");
  };

  const toggleVoice = () => {
    const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
    if (!SR) {
      alert("Voice input not supported in this browser. Try Chrome or Safari on iOS.");
      inputRef.current?.focus();
      return;
    }
    if (recording) {
      recogRef.current?.stop();
      setRecording(false);
      return;
    }
    const r = new SR();
    r.lang = "en-US";
    r.interimResults = true;
    r.continuous = false;
    r.onresult = (e) => {
      const txt = Array.from(e.results).map(x => x[0].transcript).join(" ");
      setDraft(txt);
    };
    r.onend = () => setRecording(false);
    r.onerror = () => setRecording(false);
    recogRef.current = r;
    setRecording(true);
    r.start();
  };

  return (
    <MScreen step={2}>
      <div style={{ padding: "20px 20px 8px" }}>
        <MKicker num="II.">What you want to know</MKicker>
        <MHed italic="speak it, pick it." size={30}>Write it,</MHed>
        <MDeck>Three inputs, one canvas. Use any or all.</MDeck>
      </div>

      <div style={{ margin: "14px 20px 0", border: `1px solid ${M.rule}`, background: M.paper2, padding: 14 }}>
        <div style={{ fontFamily: M.mono, fontSize: 9, letterSpacing: 2.5, color: M.mid, textTransform: "uppercase" }}>
          ▸ Your topics · {topics.length}
        </div>
        <div style={{ marginTop: 10, display: "flex", flexWrap: "wrap", gap: 6, minHeight: topics.length === 0 ? 28 : 0 }}>
          {topics.length === 0 && (
            <span style={{ fontFamily: M.display, fontStyle: "italic", fontSize: 13, color: M.mid }}>
              Nothing yet. Type, speak, or pick below.
            </span>
          )}
          {topics.map(t => (
            <span
              key={t}
              onClick={() => onRemoveTopic(t)}
              style={{
                display: "inline-flex", alignItems: "center", gap: 5,
                background: M.ink, color: M.paper, padding: "6px 12px",
                fontFamily: M.display, fontSize: 13, fontWeight: 700, borderRadius: 999,
                cursor: "pointer", userSelect: "none",
              }}>
              {t}<span style={{ color: M.amber, fontSize: 10 }}>×</span>
            </span>
          ))}
        </div>
        <div style={{ marginTop: 14, borderTop: `1px dashed ${M.rule}`, paddingTop: 12, display: "flex", alignItems: "center", gap: 10 }}>
          <div style={{ fontFamily: M.mono, fontSize: 11, color: M.mid }}>›</div>
          <input
            ref={inputRef}
            value={draft}
            onChange={e => setDraft(e.target.value)}
            onKeyDown={e => { if (e.key === "Enter") commit(); }}
            placeholder="Type a topic…"
            style={{
              flex: 1, fontFamily: M.display, fontSize: 18, letterSpacing: -0.4,
              borderBottom: `1.5px solid ${M.ink}`, paddingBottom: 3,
              border: "none", borderBottomWidth: 1.5, borderBottomStyle: "solid", borderBottomColor: M.ink,
              background: "transparent", outline: "none", color: M.ink, minWidth: 0,
            }}
          />
          <button
            onClick={toggleVoice}
            style={{
              width: 40, height: 40, borderRadius: 40, background: recording ? M.red : M.paper,
              border: `1.5px solid ${M.red}`, display: "flex", alignItems: "center", justifyContent: "center",
              boxShadow: recording ? `0 0 0 4px rgba(162,46,34,0.25)` : "none",
              flexShrink: 0, cursor: "pointer", padding: 0,
            }}>
            <div style={{ width: 10, height: 10, borderRadius: recording ? 2 : 10, background: recording ? M.paper : M.red }}/>
          </button>
          <button
            onClick={commit}
            disabled={!draft.trim()}
            style={{
              background: draft.trim() ? M.ink : M.rule2, color: draft.trim() ? M.paper : M.mid,
              border: "none", padding: "8px 12px", fontFamily: M.display, fontSize: 14, fontWeight: 700,
              cursor: draft.trim() ? "pointer" : "not-allowed", borderRadius: 2, flexShrink: 0,
            }}>
            Add
          </button>
        </div>
        {recording && (
          <div style={{ marginTop: 8, fontFamily: M.mono, fontSize: 9, letterSpacing: 2, color: M.red, fontWeight: 700 }}>
            ● LISTENING · tap to stop
          </div>
        )}
      </div>

      <div style={{ padding: "14px 20px 24px" }}>
        <div style={{ fontFamily: M.mono, fontSize: 9, letterSpacing: 2.5, color: M.mid, textTransform: "uppercase", marginBottom: 10 }}>
          — or start from the library —
        </div>
        <div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
          {TOPICS_LIBRARY.map(t => {
            const sel = topics.includes(t);
            return (
              <span
                key={t}
                onClick={() => sel ? onRemoveTopic(t) : onAddTopic(t)}
                style={{
                  padding: "6px 12px", borderRadius: 999,
                  fontFamily: M.display, fontSize: 13, cursor: "pointer", userSelect: "none",
                  border: `1px solid ${sel ? M.ink : M.rule}`,
                  background: sel ? M.ink : "transparent",
                  color: sel ? M.paper : M.ink,
                  fontWeight: sel ? 700 : 400,
                }}>
                {t}{sel && <span style={{ color: M.amber, marginLeft: 5, fontSize: 10 }}>✓</span>}
              </span>
            );
          })}
        </div>
      </div>
    </MScreen>
  );
}

// =============================================================================
// 03 · Sources
// =============================================================================
function MStep03({ sources, onToggleSource, onPickForMe }) {
  const selCount = sources.length;
  const coverage = selCount === 0 ? "—" : selCount < 3 ? "Narrow" : selCount < 7 ? "Broad" : "Very broad";
  const trust = selCount === 0 ? "—" : selCount < 3 ? "B" : selCount < 6 ? "A" : "A+";
  const desc = (() => {
    if (selCount === 0) return "pick at least one";
    const tags = new Set();
    sources.forEach(id => {
      if (["ft","econ","bloomb"].includes(id)) tags.add("fin");
      if (["nature","arxiv","nejm","mittr"].includes(id)) tags.add("research");
      if (["reuters","guard","semafor"].includes(id)) tags.add("news");
      if (["wiki","strat"].includes(id)) tags.add("analysis");
    });
    return Array.from(tags).slice(0, 2).join(" · ") || "mixed";
  })();

  return (
    <MScreen step={3}>
      <div style={{ padding: "20px 20px 8px" }}>
        <MKicker num="III.">Whom you trust</MKicker>
        <MHed italic="will carry the story." size={30}>The voices that</MHed>
        <MDeck>Pick a few, pick many, or let us choose.</MDeck>
      </div>
      <div style={{ padding: "14px 20px 8px", display: "flex", flexWrap: "wrap", gap: 6 }}>
        {SOURCES_LIBRARY.map(s => {
          const sel = sources.includes(s.id);
          return (
            <span
              key={s.id}
              onClick={() => onToggleSource(s.id)}
              style={{
                display: "inline-flex", alignItems: "center", gap: 7, padding: "5px 12px 5px 5px",
                background: sel ? M.ink : M.paper, color: sel ? M.paper : M.ink,
                border: `1px solid ${sel ? M.ink : M.rule}`, borderRadius: 999,
                fontFamily: M.display, fontSize: 12.5, fontWeight: sel ? 700 : 400,
                cursor: "pointer", userSelect: "none",
              }}>
              <span style={{
                width: 22, height: 22, borderRadius: 22, background: s.bg, color: s.fg,
                display: "inline-flex", alignItems: "center", justifyContent: "center",
                fontFamily: M.display, fontWeight: 700, fontSize: 9, border: `1px solid ${M.rule2}`,
              }}>{s.mark}</span>
              <span>{s.name}</span>
              {sel && <span style={{ color: M.amber, fontSize: 10 }}>✓</span>}
            </span>
          );
        })}
        <span
          onClick={onPickForMe}
          style={{
            display: "inline-flex", alignItems: "center", gap: 7, padding: "6px 12px",
            border: `1px dashed ${M.ink}`, borderRadius: 999, cursor: "pointer", userSelect: "none",
            fontFamily: M.display, fontStyle: "italic", fontSize: 12.5, background: M.paper2,
          }}>
          <span style={{ color: M.amber }}>✦</span>Select for me
        </span>
      </div>
      <div style={{ padding: "12px 20px 20px", borderTop: `1px dashed ${M.rule}`, margin: "6px 20px 24px" }}>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 10, marginTop: 10 }}>
          {[
            ["SELECTED", String(selCount), `of ${SOURCES_LIBRARY.length}`],
            ["COVERAGE", coverage, desc],
            ["TRUST", trust, "weighted"],
          ].map(([l,v,s]) => (
            <div key={l}>
              <div style={{ fontFamily: M.mono, fontSize: 8.5, letterSpacing: 2, color: M.mid, textTransform: "uppercase" }}>{l}</div>
              <div style={{ fontFamily: M.display, fontSize: 22, fontWeight: 700, letterSpacing: -0.4, marginTop: 2 }}>{v}</div>
              <div style={{ fontFamily: M.display, fontStyle: "italic", fontSize: 11, color: "#3a362c" }}>{s}</div>
            </div>
          ))}
        </div>
      </div>
    </MScreen>
  );
}

// -----------------------------------------------------------------------------
// Tiny style previews (unchanged visual)
// -----------------------------------------------------------------------------
function OriginalMini() {
  return (
    <div style={{ background: M.ink, color: M.paper, height: "100%", padding: 10, fontFamily: M.mono, fontSize: 7.5, letterSpacing: 0.6, lineHeight: 1.55 }}>
      <div style={{ display: "flex", justifyContent: "space-between", color: "#a29b87" }}>
        <span>▸ VOL 01 · 022</span><span style={{ color: M.amber }}>● LIVE</span>
      </div>
      <div style={{ fontFamily: M.display, fontSize: 15, fontWeight: 700, letterSpacing: -0.5, color: M.paper, marginTop: 8, lineHeight: 1.05 }}>
        Fusion's quiet <span style={{ fontStyle: "italic", color: M.amber }}>breakthrough</span>
      </div>
      <div style={{ borderTop: "1px solid #2a2f3b", marginTop: 8, paddingTop: 6, color: "#d9d2bf" }}>
        <div>› net-energy confirmed at NIF</div>
        <div>› <span style={{ color: M.amber }}>read ▸</span> Nature · 14 min</div>
      </div>
    </div>
  );
}
function EditorialMini() {
  return (
    <div style={{ background: M.paper, height: "100%", padding: 10 }}>
      <div style={{ textAlign: "center", fontFamily: M.display, fontSize: 7, letterSpacing: 3, color: M.mid, textTransform: "uppercase", borderBottom: `1px solid ${M.ink}`, paddingBottom: 3 }}>The Signal · Thursday</div>
      <div style={{ fontFamily: M.display, fontSize: 17, fontWeight: 700, letterSpacing: -0.7, lineHeight: 1, marginTop: 8, textAlign: "center" }}>
        Fusion Crosses <span style={{ fontStyle: "italic" }}>Threshold</span>
      </div>
      <div style={{ fontFamily: M.display, fontStyle: "italic", fontSize: 9, color: "#3a362c", textAlign: "center", marginTop: 4 }}>a correspondent's note</div>
      <div style={{ fontFamily: M.body, fontSize: 8, lineHeight: 1.45, marginTop: 6, columnCount: 2, columnGap: 8, color: "#1a1a1a" }}>
        Livermore reported a third net-energy shot this week, marking the clearest sign yet that…
      </div>
    </div>
  );
}
function DeveloperMini() {
  return (
    <div style={{ background: "#0d0d0d", color: "#d6d3cb", height: "100%", padding: 10, fontFamily: M.mono, fontSize: 8, lineHeight: 1.55 }}>
      <div style={{ color: "#69E397" }}># signal.md</div>
      <div style={{ color: "#7aa2f7", marginTop: 4 }}>## Fusion breakthrough</div>
      <div style={{ color: "#a29b87", marginTop: 2 }}>&gt; net-energy &times; 3 at NIF</div>
      <div style={{ color: "#d6d3cb", marginTop: 4 }}>- [x] confirmed</div>
      <div style={{ color: "#d6d3cb" }}>- [ ] commercial path</div>
      <div style={{ color: "#F2B441", marginTop: 4 }}>[source](nature.com) 14m</div>
      <div style={{ color: "#69E397", marginTop: 2 }}>$ next ▸</div>
    </div>
  );
}
function ResearchMini() {
  return (
    <div style={{ background: "#fafafa", color: "#111", height: "100%", padding: 10, fontFamily: "Inter, system-ui, sans-serif", fontSize: 8, lineHeight: 1.5 }}>
      <div style={{ fontSize: 7, letterSpacing: 1.5, color: "#666", textTransform: "uppercase" }}>Brief · 24 Apr 2026</div>
      <div style={{ fontSize: 13, fontWeight: 600, letterSpacing: -0.3, marginTop: 6, lineHeight: 1.15 }}>Fusion net-energy, repeated</div>
      <div style={{ marginTop: 6, color: "#333" }}>
        <div>— 3rd successful shot<sup>[1]</sup></div>
        <div>— 3.15 MJ yield<sup>[2]</sup></div>
        <div>— peer review pending</div>
      </div>
      <div style={{ marginTop: 6, fontSize: 7, color: "#888" }}>[1] Nature [2] LLNL brief</div>
    </div>
  );
}
function LockedMini() {
  return (
    <div style={{ background: M.paper2, height: "100%", padding: 14, display: "flex", alignItems: "center", justifyContent: "center", position: "relative" }}>
      <div style={{ position: "absolute", inset: 0, backgroundImage: `repeating-linear-gradient(45deg, transparent 0, transparent 7px, rgba(15,14,12,0.05) 7px, rgba(15,14,12,0.05) 8px)` }}/>
      <div style={{ position: "relative", textAlign: "center" }}>
        <div style={{ fontSize: 22 }}>◐</div>
        <div style={{ fontFamily: M.display, fontStyle: "italic", fontSize: 13, fontWeight: 700, marginTop: 6 }}>Your style</div>
        <div style={{ fontFamily: M.mono, fontSize: 7, letterSpacing: 1.5, color: M.mid, marginTop: 4, textTransform: "uppercase" }}>Premium · soon</div>
      </div>
    </div>
  );
}

const STYLE_MINIS = {
  original:  <OriginalMini/>,
  editorial: <EditorialMini/>,
  developer: <DeveloperMini/>,
  research:  <ResearchMini/>,
  custom:    <LockedMini/>,
};

// =============================================================================
// 04 · Style (tap-to-select carousel)
// =============================================================================
function MStep04({ style, onPickStyle }) {
  return (
    <MScreen step={4}>
      <div style={{ padding: "20px 20px 8px" }}>
        <MKicker num="IV.">How it looks in your inbox</MKicker>
        <MHed italic="is a choice." size={28}>The form of intelligence</MHed>
        <MDeck>Same signal. Four voices. Tap to select.</MDeck>
      </div>
      <div style={{ padding: "14px 0 6px", overflowX: "auto", display: "flex", gap: 12, scrollSnapType: "x mandatory", WebkitOverflowScrolling: "touch" }}>
        {STYLES_LIBRARY.map((s, i) => {
          const sel = style === s.id;
          return (
            <div
              key={s.id}
              onClick={() => { if (!s.locked) onPickStyle(s.id); }}
              style={{
                minWidth: 220, marginLeft: i === 0 ? 20 : 0, marginRight: i === STYLES_LIBRARY.length - 1 ? 20 : 0,
                border: `1.5px solid ${sel ? M.ink : M.rule}`, background: M.paper2,
                outline: sel ? `3px solid ${M.amber}` : "none", outlineOffset: 2,
                scrollSnapAlign: "start", position: "relative",
                opacity: s.locked ? 0.7 : 1,
                cursor: s.locked ? "not-allowed" : "pointer",
                flexShrink: 0,
              }}>
              {s.locked && (
                <div style={{ position: "absolute", top: 8, right: 8, zIndex: 2, background: M.ink, color: M.paper, padding: "3px 7px", fontFamily: M.mono, fontSize: 8, letterSpacing: 1.5 }}>◐ PREMIUM</div>
              )}
              <div style={{ height: 180, borderBottom: `1px solid ${M.rule}`, overflow: "hidden" }}>{STYLE_MINIS[s.id]}</div>
              <div style={{ padding: 12 }}>
                <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline" }}>
                  <div style={{ fontFamily: M.display, fontSize: 18, fontWeight: 700, letterSpacing: -0.5 }}>
                    {s.name}{s.italic && <span style={{ fontStyle: "italic", color: M.navy }}> {s.italic}</span>}
                  </div>
                  {sel && <div style={{ fontFamily: M.mono, fontSize: 8, letterSpacing: 1.8, color: M.amber, fontWeight: 700 }}>✓ SELECTED</div>}
                </div>
              </div>
            </div>
          );
        })}
      </div>
      <div style={{ padding: "10px 20px 24px", display: "flex", gap: 5, justifyContent: "center" }}>
        {STYLES_LIBRARY.map(s => (
          <div key={s.id} style={{
            width: style === s.id ? 18 : 6, height: 6, borderRadius: 6,
            background: style === s.id ? M.ink : M.rule2,
          }}/>
        ))}
      </div>
    </MScreen>
  );
}

// =============================================================================
// 05 · Frequency — segmented + day grid + live preview
// =============================================================================
function computeNextEditions(days, hour, minute, count = 4) {
  if (!days || days.length === 0) return [];
  const out = [];
  const now = new Date();
  const start = new Date(now);
  start.setSeconds(0, 0);
  for (let off = 0; out.length < count && off < 30; off++) {
    const d = new Date(start);
    d.setDate(d.getDate() + off);
    const dow = (d.getDay() + 6) % 7;
    if (!days.includes(dow)) continue;
    d.setHours(hour, minute, 0, 0);
    if (off === 0 && d <= now) continue;
    out.push(d);
  }
  return out;
}

function MStep05({ frequency, days, hour, minute, onSetFrequency, onToggleDay }) {
  const editions = computeNextEditions(days, hour, minute);
  const sendCount = days.length;
  const weekdays = days.every(d => d < 5) && sendCount === 5;
  const desc = sendCount === 0 ? "no sends yet"
    : sendCount === 7 ? "every day"
    : weekdays ? "weekends off"
    : `${sendCount} send${sendCount === 1 ? "" : "s"}/wk`;

  return (
    <MScreen step={5}>
      <div style={{ padding: "20px 20px 8px" }}>
        <MKicker num="V.">Your rhythm</MKicker>
        <MHed italic="the week." size={30}>Daily, or once</MHed>
        <MDeck>Tap a segment; toggle days below.</MDeck>
      </div>

      <div style={{ margin: "14px 20px 0", border: `1px solid ${M.ink}`, display: "flex", background: M.paper }}>
        {["daily", "weekly"].map(f => {
          const sel = frequency === f;
          return (
            <div
              key={f}
              onClick={() => onSetFrequency(f)}
              style={{
                flex: 1,
                background: sel ? M.ink : "transparent",
                color: sel ? M.paper : M.mid,
                padding: "12px 0", textAlign: "center",
                fontFamily: M.display, fontSize: 16, fontWeight: sel ? 700 : 400,
                cursor: "pointer", userSelect: "none",
                textTransform: "capitalize",
              }}>
              {f} {sel && <span style={{ fontFamily: M.mono, fontSize: 9, color: M.amber, letterSpacing: 1.5 }}>✓</span>}
            </div>
          );
        })}
      </div>

      <div style={{ padding: "20px 20px 6px", display: "grid", gridTemplateColumns: "repeat(7, 1fr)", gap: 6 }}>
        {DAY_LABELS.map((label, i) => {
          const sel = days.includes(i);
          const today = new Date();
          const dowToday = (today.getDay() + 6) % 7;
          const offset = (i - dowToday + 7) % 7;
          const date = new Date(today);
          date.setDate(today.getDate() + offset);
          return (
            <div
              key={i}
              onClick={() => onToggleDay(i)}
              style={{
                aspectRatio: "1/1.1", cursor: "pointer", userSelect: "none",
                background: sel ? M.ink : M.paper,
                color: sel ? M.paper : M.ink,
                border: `1px solid ${sel ? M.ink : M.rule}`,
                display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center",
              }}>
              <div style={{ fontFamily: M.mono, fontSize: 8, letterSpacing: 1, opacity: 0.7 }}>{label}</div>
              <div style={{ fontFamily: M.display, fontSize: 16, fontWeight: 700, marginTop: 1 }}>{date.getDate()}</div>
              <div style={{ fontFamily: M.mono, fontSize: 7, letterSpacing: 1, color: sel ? M.amber : M.mid }}>
                {sel ? "SEND" : "—"}
              </div>
            </div>
          );
        })}
      </div>
      <div style={{ padding: "4px 20px", fontFamily: M.mono, fontSize: 9, letterSpacing: 2, color: M.mid, textTransform: "uppercase" }}>
        ▸ {sendCount} send{sendCount === 1 ? "" : "s"} · {desc}
      </div>

      <div style={{ padding: "18px 20px 24px", borderTop: `1px dashed ${M.rule}`, marginTop: 12 }}>
        <div style={{ fontFamily: M.mono, fontSize: 9, letterSpacing: 2, color: M.mid, textTransform: "uppercase", marginBottom: 8 }}>
          ▸ Next {editions.length} edition{editions.length === 1 ? "" : "s"}
        </div>
        {editions.length === 0 ? (
          <div style={{ fontFamily: M.display, fontStyle: "italic", fontSize: 14, color: M.mid, padding: "8px 0" }}>
            Select at least one day.
          </div>
        ) : (
          <div style={{ display: "flex", flexDirection: "column", gap: 2, fontFamily: M.body, fontSize: 13 }}>
            {editions.map(d => {
              const dow = (d.getDay() + 6) % 7;
              const label = `${DAY_NAMES_FULL[dow]} ${d.getDate()} ${MONTH_ABBR[d.getMonth()]}`;
              const time = `${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
              return (
                <div key={d.toISOString()} style={{ display: "flex", justifyContent: "space-between", padding: "6px 0", borderBottom: `1px dotted ${M.rule2}` }}>
                  <span style={{ fontFamily: M.display, fontStyle: "italic" }}>{label}</span>
                  <span style={{ fontFamily: M.mono, fontSize: 11, color: M.mid }}>{time}</span>
                </div>
              );
            })}
          </div>
        )}
      </div>
    </MScreen>
  );
}

// =============================================================================
// 06 · Time — tap-to-select hour + minute
// =============================================================================
function MStep06({ hour, minute, evening, onSetHour, onSetMinute, onToggleEvening }) {
  return (
    <MScreen step={6}>
      <div style={{ padding: "20px 20px 8px" }}>
        <MKicker num="VI.">When it arrives</MKicker>
        <MHed italic="becomes a ritual." size={30}>The hour it</MHed>
        <MDeck>Tap to choose. One send, or add an evening digest.</MDeck>
      </div>
      <div style={{ padding: "18px 20px 8px", textAlign: "center" }}>
        <div style={{ fontFamily: M.display, fontSize: 84, fontWeight: 700, letterSpacing: -4, lineHeight: 1 }}>
          {pad2(hour)}<span style={{ color: M.amber }}>:</span>{pad2(minute)}
        </div>
        <div style={{ fontFamily: M.display, fontStyle: "italic", fontSize: 14, color: "#3a362c", marginTop: 4 }}>
          local · {Intl.DateTimeFormat().resolvedOptions().timeZone}
        </div>
      </div>

      <div style={{ margin: "10px 20px 0", border: `1px solid ${M.rule}`, background: M.paper2, display: "grid", gridTemplateColumns: "1fr 40px 1fr" }}>
        <div style={{ padding: "10px 0", maxHeight: 200, overflowY: "auto" }}>
          {HOURS.map(h => {
            const sel = h === hour;
            return (
              <div
                key={h}
                onClick={() => onSetHour(h)}
                style={{
                  padding: "8px 0", textAlign: "center", cursor: "pointer", userSelect: "none",
                  fontFamily: M.display, fontSize: sel ? 30 : 20,
                  fontWeight: sel ? 700 : 400,
                  color: sel ? M.ink : M.mid,
                  background: sel ? `rgba(184,117,26,0.1)` : "transparent",
                  borderTop: sel ? `1px solid ${M.ink}` : "none",
                  borderBottom: sel ? `1px solid ${M.ink}` : "none",
                  letterSpacing: -1,
                }}>
                {pad2(h)}
              </div>
            );
          })}
        </div>
        <div style={{ display: "flex", alignItems: "center", justifyContent: "center", fontFamily: M.display, fontSize: 28, fontWeight: 700, color: M.amber }}>:</div>
        <div style={{ padding: "10px 0", maxHeight: 200, overflowY: "auto" }}>
          {MINUTES.map(m => {
            const sel = m === minute;
            return (
              <div
                key={m}
                onClick={() => onSetMinute(m)}
                style={{
                  padding: "8px 0", textAlign: "center", cursor: "pointer", userSelect: "none",
                  fontFamily: M.display, fontSize: sel ? 30 : 20,
                  fontWeight: sel ? 700 : 400,
                  color: sel ? M.ink : M.mid,
                  background: sel ? `rgba(184,117,26,0.1)` : "transparent",
                  borderTop: sel ? `1px solid ${M.ink}` : "none",
                  borderBottom: sel ? `1px solid ${M.ink}` : "none",
                  letterSpacing: -1,
                }}>
                {pad2(m)}
              </div>
            );
          })}
        </div>
      </div>

      <div
        onClick={onToggleEvening}
        style={{
          margin: "14px 20px 24px", border: `1px ${evening ? "solid" : "dashed"} ${evening ? M.amber : M.rule}`,
          background: evening ? `rgba(184,117,26,0.08)` : "transparent",
          padding: 14, display: "flex", justifyContent: "space-between", alignItems: "center",
          cursor: "pointer", userSelect: "none",
        }}>
        <div>
          <div style={{ fontFamily: M.mono, fontSize: 9, letterSpacing: 2, color: M.amber, fontWeight: 700, textTransform: "uppercase" }}>
            ▸ {evening ? "Evening on" : "Add evening"}
          </div>
          <div style={{ fontFamily: M.display, fontStyle: "italic", fontSize: 12.5, color: "#3a362c", marginTop: 2 }}>
            A closing note at 17:30.
          </div>
        </div>
        <div style={{ fontFamily: M.display, fontSize: 26, fontWeight: 700, color: M.amber }}>
          {evening ? "✓" : "+"}
        </div>
      </div>
    </MScreen>
  );
}

// =============================================================================
// 07 · Processing — auto-advance
// =============================================================================
function MStep07({ onDone, sources }) {
  const [progress, setProgress] = useState(0);
  useEffect(() => {
    const start = Date.now();
    const dur = 2800;
    const tick = setInterval(() => {
      const p = Math.min(1, (Date.now() - start) / dur);
      setProgress(p);
      if (p >= 1) {
        clearInterval(tick);
        setTimeout(() => onDone && onDone(), 200);
      }
    }, 50);
    return () => clearInterval(tick);
  }, []);

  const srcLabels = (sources || []).slice(0, 4).map(id => {
    const s = SOURCES_LIBRARY.find(x => x.id === id);
    return s?.name || id;
  });

  return (
    <MScreen step={7} scroll={false} dark>
      <div style={{ flex: 1, background: M.ink, color: M.paper, position: "relative", overflow: "hidden", display: "flex", flexDirection: "column" }}>
        <div style={{ flex: 1, position: "relative", display: "flex", alignItems: "center", justifyContent: "center" }}>
          <svg width="340" height="340" viewBox="-170 -170 340 340">
            {[40,72,104,136,160].map((r,i) => (
              <circle key={r} cx="0" cy="0" r={r} fill="none" stroke={M.amber} strokeWidth="0.8" opacity={0.22 - i * 0.03}>
                <animate attributeName="r" from={r - 10} to={r + 10} dur={`${3 + i * 0.3}s`} repeatCount="indefinite"/>
                <animate attributeName="opacity" values={`${0.25 - i*0.03};0;${0.25 - i*0.03}`} dur={`${3 + i * 0.3}s`} repeatCount="indefinite"/>
              </circle>
            ))}
            {Array.from({ length: 18 }).map((_, i) => {
              const a = (i / 18) * 2 * Math.PI;
              return <line key={i} x1={Math.cos(a)*28} y1={Math.sin(a)*28} x2={Math.cos(a)*160} y2={Math.sin(a)*160} stroke={M.paper} strokeWidth="0.3" opacity="0.12"/>;
            })}
            <circle cx="0" cy="0" r="24" fill="none" stroke={M.amber} strokeWidth="1.5"/>
            <circle cx="0" cy="0" r="13" fill={M.amber}>
              <animate attributeName="opacity" values="0.9;0.45;0.9" dur="1.6s" repeatCount="indefinite"/>
            </circle>
            <circle cx="0" cy="0" r="5" fill={M.paper}/>
          </svg>
          {srcLabels[0] && <div style={{ position: "absolute", top: 24, left: 24, fontFamily: M.mono, fontSize: 9, letterSpacing: 1.5, color: "#a29b87", textTransform: "uppercase" }}>{srcLabels[0]}</div>}
          {srcLabels[1] && <div style={{ position: "absolute", top: 48, right: 24, fontFamily: M.mono, fontSize: 9, letterSpacing: 1.5, color: "#a29b87", textTransform: "uppercase" }}>{srcLabels[1]}</div>}
          {srcLabels[2] && <div style={{ position: "absolute", bottom: 90, left: 24, fontFamily: M.mono, fontSize: 9, letterSpacing: 1.5, color: "#a29b87", textTransform: "uppercase" }}>{srcLabels[2]}</div>}
          {srcLabels[3] && <div style={{ position: "absolute", bottom: 70, right: 30, fontFamily: M.mono, fontSize: 9, letterSpacing: 1.5, color: "#a29b87", textTransform: "uppercase" }}>{srcLabels[3]}</div>}
        </div>
        <div style={{ padding: "20px", borderTop: `1px solid #2a2f3b` }}>
          <div style={{ fontFamily: M.mono, fontSize: 9.5, letterSpacing: 2.5, color: M.amber, fontWeight: 700 }}>
            ▸ PHASE {pad2(Math.min(4, Math.ceil(progress * 4)))} / 04 · SYNTHESIS
          </div>
          <div style={{ fontFamily: M.display, fontSize: 26, fontWeight: 700, letterSpacing: -1, marginTop: 6, lineHeight: 1 }}>
            Listening for the <span style={{ fontStyle: "italic", color: M.amber }}>hum.</span>
          </div>
          <div style={{ marginTop: 10, height: 2, background: "#2a2f3b", position: "relative" }}>
            <div style={{ position: "absolute", left: 0, top: 0, bottom: 0, width: `${progress * 100}%`, background: M.paper, transition: "width 0.05s linear" }}/>
            <div style={{ position: "absolute", left: `${progress * 100}%`, top: -3, width: 2, height: 8, background: M.amber }}/>
          </div>
          <div style={{ marginTop: 10, fontFamily: M.mono, fontSize: 10, color: "#a29b87", letterSpacing: 1.2, lineHeight: 1.6 }}>
            <div style={{ color: "#69E397" }}>[0705Z] ✓ {Math.max(1, Math.floor(progress * 6))} patterns detected</div>
            <div style={{ color: "#F2B441" }}>[0705Z] ▸ forming headline<span style={{ display: "inline-block", width: 6, height: 10, background: "#F2B441", marginLeft: 2, verticalAlign: "middle", animation: "ms-caret 1s step-end infinite" }}/></div>
          </div>
        </div>
      </div>
    </MScreen>
  );
}

// =============================================================================
// 08 · Confirmation
// =============================================================================
function MStep08({ prefs, onFinish, sendStatus, sendError }) {
  const styleMeta = STYLES_LIBRARY.find(s => s.id === prefs.style) || STYLES_LIBRARY[0];
  const topicsPreview = prefs.topics.length === 0 ? "—" : prefs.topics.slice(0, 2).join(" · ") + (prefs.topics.length > 2 ? " · …" : "");
  const sourcesPreview = (() => {
    const names = prefs.sources.map(id => SOURCES_LIBRARY.find(s => s.id === id)?.name).filter(Boolean);
    if (names.length === 0) return "—";
    return names.slice(0, 2).join(" · ") + (names.length > 2 ? " · …" : "");
  })();
  const dayDesc = (() => {
    const n = prefs.days.length;
    if (n === 0) return "no days";
    if (n === 7) return "every day";
    if (n === 5 && prefs.days.every(d => d < 5)) return "Mon–Fri";
    return prefs.days.map(d => DAY_NAMES_FULL[d].slice(0,2)).join(" ");
  })();
  const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;

  return (
    <MScreen step={8}>
      <div style={{ padding: "22px 20px 8px" }}>
        <MKicker num="VIII.">It's set</MKicker>
        <MHed italic="Millennium Signal." size={30}>You'll receive your</MHed>
      </div>
      <div style={{ margin: "14px 20px 0", border: `1px solid ${M.rule}`, background: M.paper2, padding: 16 }}>
        <div style={{ textAlign: "center", borderBottom: `3px double ${M.ink}`, paddingBottom: 8 }}>
          <div style={{ fontFamily: M.mono, fontSize: 8.5, letterSpacing: 2.5, color: M.mid, textTransform: "uppercase" }}>
            ✦ Subscription · №{String(Math.floor(Math.random() * 900 + 100))} ✦
          </div>
          <div style={{ fontFamily: M.display, fontSize: 22, fontWeight: 700, letterSpacing: -0.8, marginTop: 2 }}>
            The <span style={{ fontStyle: "italic", color: M.navy }}>Signal</span>, for you
          </div>
        </div>
        <div style={{ marginTop: 12, display: "grid", gridTemplateColumns: "1fr 1fr", rowGap: 12, columnGap: 16 }}>
          {[
            ["Frequency", prefs.frequency === "daily" ? "Daily" : "Weekly", dayDesc],
            ["Delivery", `${pad2(prefs.hour)}:${pad2(prefs.minute)}`, tz.split("/").pop().replace("_"," ")],
            ["Style", styleMeta.name, styleMeta.italic ? styleMeta.italic.toLowerCase() : "—"],
            ["Topics", String(prefs.topics.length), topicsPreview],
            ["Sources", String(prefs.sources.length), sourcesPreview],
            ["Edition", "Ready", "on demand"],
          ].map(([l,v,s], i) => (
            <div key={l} style={{ borderTop: `1px solid ${M.rule2}`, paddingTop: 6 }}>
              <div style={{ fontFamily: M.mono, fontSize: 8, letterSpacing: 2, color: M.mid, textTransform: "uppercase" }}>{l}</div>
              <div style={{ fontFamily: M.display, fontSize: 16, fontWeight: 700, letterSpacing: -0.4, marginTop: 1, color: i === 5 ? M.amber : M.ink }}>{v}</div>
              <div style={{ fontFamily: M.display, fontStyle: "italic", fontSize: 11, color: "#3a362c" }}>{s}</div>
            </div>
          ))}
        </div>
        <div style={{ marginTop: 12, borderTop: `1px dashed ${M.rule}`, paddingTop: 8, fontFamily: M.mono, fontSize: 8.5, letterSpacing: 1.8, color: M.mid, textTransform: "uppercase", textAlign: "center" }}>
          Edit any of this, any time →
        </div>
      </div>
      <div style={{ margin: "14px 20px 24px", background: M.ink, color: M.paper, padding: 18, position: "relative", overflow: "hidden" }}>
        <div style={{ position: "absolute", top: -50, right: -50, width: 180, height: 180, borderRadius: 999, background: `radial-gradient(circle, rgba(184,117,26,0.35), transparent 70%)` }}/>
        <div style={{ position: "relative", fontFamily: M.mono, fontSize: 9, letterSpacing: 2.5, color: M.amber, fontWeight: 700, textTransform: "uppercase" }}>
          ✦ {sendStatus === "sending" ? "Queueing your research…" : sendStatus === "error" ? "Did not queue" : "Run your signal"}
        </div>
        <div style={{ position: "relative", fontFamily: M.display, fontSize: 28, fontWeight: 700, letterSpacing: -1, lineHeight: 1.05, marginTop: 8 }}>
          {sendStatus === "sending"
            ? <>Researching <span style={{ fontStyle: "italic", color: M.amber }}>now…</span></>
            : <>Real research. <span style={{ fontStyle: "italic", color: M.amber }}>PDF in your inbox.</span></>
          }
        </div>
        <div style={{ position: "relative", fontFamily: M.display, fontStyle: "italic", fontSize: 13, color: "#d9d2bf", marginTop: 6 }}>
          Going to <span style={{ color: M.amber }}>{prefs.email || "your inbox"}</span> · A4 Blueprint PDF · arrives in 10–20 min
        </div>
        {sendError && (
          <div style={{ position: "relative", marginTop: 12, padding: 10, background: "rgba(162,46,34,0.18)", border: `1px solid ${M.red}`, fontFamily: M.mono, fontSize: 10.5, color: "#f3d8d3", lineHeight: 1.5, whiteSpace: "pre-wrap" }}>
            {sendError}
          </div>
        )}
        <div style={{ position: "relative", marginTop: 14 }}>
          <MBtn
            amber
            disabled={sendStatus === "sending"}
            onClick={onFinish}>
            {sendStatus === "sending" ? "Queueing…" : sendStatus === "error" ? "Try again" : "Generate my signal"}
          </MBtn>
        </div>
      </div>
    </MScreen>
  );
}

Object.assign(window, {
  MStep01, MStep02, MStep03, MStep04, MStep05, MStep06, MStep07, MStep08,
  MScreen, MKicker, MHed, MDeck, MBtn,
  MS_TOPICS_LIBRARY: TOPICS_LIBRARY,
  MS_SOURCES_LIBRARY: SOURCES_LIBRARY,
  MS_DEFAULT_SOURCE_IDS: DEFAULT_SOURCE_IDS,
  MS_STYLES_LIBRARY: STYLES_LIBRARY,
});
