/* ============================================================
   Guided Workshop — Creator-as-supporter overlay (shared)

   The completer drives the workshop. The creator (now acting as
   supporter) can drop into any live module and co-pilot from the
   same UI: live presence, inline comment threads, pinned guidance
   notes, example answers from past submissions, click-to-
   highlight, and "take over" the active question to fill it in
   for the completer.

   Exports onto window:
     useSupporter(initial)              hook + demo data
     SupporterSessionBanner             sticky top banner
     SupporterDock                      floating action cluster
     SupporterGuidanceCard              pinned creator note
     SupporterCoPilotBar                sticky bottom bar (sup. only)
     SupporterTakeoverPulse             "supporter is editing" cue
     SupporterPerspectiveTweaks         tweak controls

   The visual chrome is themed by CSS variables declared in the
   host page, so the same components fit both v1 (slate-on-white)
   and v2 (rounded surfaces). See colors_and_type.css consumers.
   ============================================================ */

const { useState: supUseState, useEffect: supUseEffect, useCallback: supUseCallback, useRef: supUseRef, useMemo: supUseMemo } = React;

/* ---------- Demo people ---------- */
const SUPPORTER_PEOPLE = {
  completer: { id: "completer", name: "Adaeze Nwosu",   role: "Compliance Lead",  avatar: 47, hue: "#175cd3" },
  supporter: { id: "supporter", name: "Adetola Okafor", role: "Module creator",   avatar: 14, hue: "#7a5af8" },
};

/* ---------- Pre-written guidance notes (authored by the creator
   when the module was built). Visible to both perspectives but
   styled as a soft hint to the completer and as a reference to
   the supporter. ---------- */
const SUPPORTER_GUIDANCE = {
  "1.1": {
    note: "This opening sets context — there's no answer expected here. If the completer asks what 'maturity' means, point them at 2.1 before they go further.",
    examples: [],
    examplesSource: null,
    keypoints: ["Skim, don't dwell"],
  },
  "4.1": {
    note: "Use evidence the completer has on hand. If they're missing a register, suggest exporting from their asset management tool rather than starting from scratch.",
    examples: [
      "Annual asset inventory + reconciliation",
      "Tag-and-track for items > $5k",
      "Quarterly disposal log signed off by Finance",
    ],
    examplesSource: { kind: "curated", label: "Curated by the module creator · visible to supporters only" },
    keypoints: ["Sources of truth", "Reconciliation cadence", "Sign-off owner"],
  },
  "4.2": {
    note: "Score against current maturity, not the target. People over-rate themselves here. If there's data but no defined process, that's still 'Initial' — say so.",
    examples: [
      "Initial — data exists, no defined process",
      "Repeatable — process documented, owner assigned",
      "Defined — process measured + reviewed annually",
    ],
    examplesSource: { kind: "aggregate", label: "Anonymised from 41 submissions across 9 organisations" },
    keypoints: ["Honest read", "Process, not artifacts"],
  },
  "5.1": {
    note: "Open the poll and share the join code on screen. Don't read the question out loud — let people answer cold.",
    examples: [],
    examplesSource: null,
    keypoints: ["Open before reading", "Anonymous responses"],
  },
  "6.1": {
    note: "Common over-answer. Keep it to one bottleneck, not three. If they list more than one, ask which they'd fix this quarter.",
    examples: [
      "Inventory accuracy drift between systems",
      "No single owner for write-offs",
      "Audit trail breaks at vendor handover",
    ],
    examplesSource: { kind: "aggregate", label: "Anonymised from 28 submissions across 6 organisations" },
    keypoints: ["One bottleneck", "Actionable today"],
  },
};

/* Demo seed comments anchored to specific item ids. */
const SUPPORTER_COMMENTS_DEMO = {
  "4.2": [
    { who: "completer", text: "I'm not sure how to score this — we have a register but no defined process around it.", ts: "2m ago" },
    { who: "supporter", text: "Honest read is 'Initial' then. The label asks about the system, not whether you have data. Pick Initial and drop a note in the comment so we remember why.", ts: "1m ago" },
    { who: "completer", text: "Got it — going with Initial.", ts: "just now" },
  ],
  "4.1": [
    { who: "supporter", text: "Adaeze — when you get to this one ping me. I've got a template that'll save you 20 mins.", ts: "5m ago" },
  ],
};

/* ---------- Hook ---------- */
function useSupporter(initialPerspective = "completer") {
  const [perspective, setPerspective] = supUseState(initialPerspective);
  const [sessionLive, setSessionLive] = supUseState(true);
  const [comments, setComments]       = supUseState(SUPPORTER_COMMENTS_DEMO);
  const [highlights, setHighlights]   = supUseState({});
  const [takenOverItemId, setTakenOver] = supUseState(null);
  const [openPanel, setOpenPanel]     = supUseState(null);

  /* Completer-side help signals.
     stuckItems: Set of item ids flagged as "I'm stuck".
     helpRequested: { ts, note } | null when completer has summoned the creator.
     Feed the supporter dock + the live-sessions panel on creator-modules.html. */
  const [stuckItems, setStuckItems] = supUseState(() => new Set());
  const [helpRequested, setHelpRequested] = supUseState(null);

  const flagStuck = supUseCallback((id, note) => {
    setStuckItems(prev => { const n = new Set(prev); n.add(id); return n; });
    if (note && note.trim()) {
      setComments(prev => ({
        ...prev,
        [id]: [...(prev[id] || []), { who: "completer", text: "🚩 " + note.trim(), ts: "just now" }],
      }));
    }
  }, []);
  const unflagStuck = supUseCallback((id) => {
    setStuckItems(prev => { const n = new Set(prev); n.delete(id); return n; });
  }, []);
  const requestHelp = supUseCallback((note) => {
    setHelpRequested({ ts: Date.now(), note: (note || "").trim() });
  }, []);
  const cancelHelpRequest = supUseCallback(() => setHelpRequested(null), []);

  const me   = SUPPORTER_PEOPLE[perspective];
  const them = SUPPORTER_PEOPLE[perspective === "completer" ? "supporter" : "completer"];

  const addComment = supUseCallback((itemId, text) => {
    if (!text || !text.trim()) return;
    setComments(prev => ({
      ...prev,
      [itemId]: [...(prev[itemId] || []), { who: perspective, text: text.trim(), ts: "just now" }],
    }));
  }, [perspective]);

  const takeover        = supUseCallback((itemId) => setTakenOver(itemId), []);
  const releaseTakeover = supUseCallback(() => setTakenOver(null), []);

  /* Highlight = "I'm pointing here" toggle. One per item: clicking again
     un-pins it. This deliberately ignores any old multi-highlight semantics
     so the strip can't lie about counts. */
  const togglePoint = supUseCallback((itemId) => {
    setHighlights(prev => {
      const next = { ...prev };
      if (next[itemId] && next[itemId].length > 0) delete next[itemId];
      else next[itemId] = [{ by: perspective, label: "pointed at this question", ts: "just now" }];
      return next;
    });
  }, [perspective]);
  const addHighlight = togglePoint; /* legacy name */
  const clearHighlights = supUseCallback((itemId) => {
    setHighlights(prev => { const n = { ...prev }; delete n[itemId]; return n; });
  }, []);

  const commentCount = supUseCallback((id) => (comments[id] || []).length, [comments]);
  const guidanceFor  = supUseCallback((id) => SUPPORTER_GUIDANCE[id] || null, []);

  /* Total unread badge for the supporter — counts items with completer comments */
  const totalThreads = supUseMemo(() => Object.keys(comments).length, [comments]);

  return {
    perspective, setPerspective,
    sessionLive, setSessionLive,
    me, them,
    comments, addComment, commentCount, totalThreads,
    guidance: SUPPORTER_GUIDANCE, guidanceFor,
    highlights, addHighlight, clearHighlights,
    takenOverItemId, takeover, releaseTakeover,
    openPanel, setOpenPanel,
    stuckItems, flagStuck, unflagStuck,
    helpRequested, requestHelp, cancelHelpRequest,
  };
}

/* ---------- Tiny presentational helpers ---------- */
function SupAvatar({ id, size = 28, ring, online = true, title }) {
  return (
    <span className={`sup-av${ring ? " sup-av--" + ring : ""}`} style={{ width: size, height: size }} title={title || ""}>
      <img src={`https://i.pravatar.cc/96?img=${id}`} alt="" />
      {online && <span className="sup-av-dot" />}
    </span>
  );
}

function SupIcon({ name, size = 16 }) {
  const stroke = { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" };
  switch (name) {
    case "comment":   return <svg {...stroke}><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg>;
    case "guidance":  return <svg {...stroke}><circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/></svg>;
    case "examples":  return <svg {...stroke}><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>;
    case "highlight": return <svg {...stroke}><path d="M12 20l-9-9 7-7 9 9-7 7zM4 22h16"/></svg>;
    case "takeover":  return <svg {...stroke}><path d="M3 17l6-6 4 4 8-8"/><polyline points="14 7 21 7 21 14"/></svg>;
    case "close":     return <svg {...stroke}><line x1="18" y1="6"  x2="6"  y2="18"/><line x1="6"  y1="6"  x2="18" y2="18"/></svg>;
    case "send":      return <svg {...stroke}><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>;
    case "expand":    return <svg {...stroke}><polyline points="6 9 12 15 18 9"/></svg>;
    case "live":      return <svg {...stroke}><circle cx="12" cy="12" r="3" fill="currentColor"/><circle cx="12" cy="12" r="8"/></svg>;
    case "leave":     return <svg {...stroke}><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>;
    case "bot":       return <svg {...stroke}><rect x="3" y="11" width="18" height="10" rx="2"/><circle cx="12" cy="5" r="2"/><path d="M12 7v4M8 16h.01M16 16h.01"/></svg>;
    default:          return null;
  }
}

/* =====================================================================
   SessionBanner — sticky top strip when a co-pilot session is live
   ===================================================================== */
function SupporterSessionBanner({ sup, moduleName = "Asset Management" }) {
  if (!sup.sessionLive) return null;
  const isCompleter = sup.perspective === "completer";
  /* Tight one-line banner. Drops the module name (already in the page
     header) and the "module creator" subtitle (already on the Creator
     badge). Layout:
       [pulse · LIVE] [avatar pair] You & <other name> · CREATOR   [End session] */
  return (
    <div className="sup-banner" data-screen-label="Co-pilot session banner">
      <div className="sup-banner-inner">
        <span className="sup-banner-status">
          <span className="sup-live-dot" aria-hidden="true" />
          <span className="sup-banner-eyebrow">Live co-pilot</span>
        </span>

        <div className="sup-banner-presence">
          <SupAvatar id={SUPPORTER_PEOPLE.completer.avatar} size={26} ring="completer" />
          <SupAvatar id={SUPPORTER_PEOPLE.supporter.avatar} size={26} ring="supporter" />
          <span className="sup-banner-names">
            <b style={{ color: isCompleter ? SUPPORTER_PEOPLE.completer.hue : SUPPORTER_PEOPLE.supporter.hue }}>
              You
            </b>
            <span className="sup-banner-amp">&amp;</span>
            <b style={{ color: sup.them.hue }}>{sup.them.name}</b>
            <span className={`sup-pill ${sup.them.id === "supporter" ? "sup-pill-creator" : "sup-pill-mute"}`}>
              {sup.them.id === "supporter" ? "Creator" : "Completer"}
            </span>
          </span>
        </div>

        <button className="sup-btn sup-btn-ghost sup-banner-end" type="button" onClick={() => sup.setSessionLive(false)} title="End session">
          <SupIcon name="leave" size={14} />
          <span>{isCompleter ? "End session" : "Leave"}</span>
        </button>
      </div>
    </div>
  );
}

/* =====================================================================
   GuidanceCard — pinned, inline above question content
   ===================================================================== */
function SupporterGuidanceCard({ sup, itemId }) {
  const g = sup.guidanceFor(itemId);
  if (!g) return null;
  const isSupporter = sup.perspective === "supporter";
  const [expanded, setExpanded] = supUseState(isSupporter); /* default open for supporter */
  supUseEffect(() => { setExpanded(isSupporter); }, [itemId, isSupporter]);

  return (
    <aside className={`sup-guidance ${expanded ? "is-expanded" : ""} ${isSupporter ? "for-supporter" : "for-completer"}`}>
      <button className="sup-guidance-head" type="button" onClick={() => setExpanded(v => !v)}>
        <span className="sup-guidance-badge">
          <SupIcon name="guidance" size={14} />
          <span>{isSupporter ? "Creator note (your draft)" : `Note from ${SUPPORTER_PEOPLE.supporter.name}`}</span>
        </span>
        <span className="sup-guidance-chev"><SupIcon name="expand" size={14} /></span>
      </button>
      {expanded && (
        <div className="sup-guidance-body">
          <p className="sup-guidance-note">{g.note}</p>
          {g.keypoints && g.keypoints.length > 0 && (
            <ul className="sup-guidance-keys">
              {g.keypoints.map((k, i) => <li key={i}>{k}</li>)}
            </ul>
          )}
          {g.examples && g.examples.length > 0 && (
            <details className="sup-guidance-examples">
              <summary>
                <span className="sup-ex-label">Past responses to this question</span>
                <span className="sup-count">{g.examples.length}</span>
                {g.examplesSource && (
                  <span className={`sup-ex-tag sup-ex-tag-${g.examplesSource.kind}`}>
                    {g.examplesSource.kind === "curated"   ? "Curated"
                    : g.examplesSource.kind === "aggregate" ? "Anonymised"
                    : "Named"}
                  </span>
                )}
              </summary>
              {g.examplesSource && (
                <div className="sup-ex-source">
                  <span className="sup-ex-source-icon" aria-hidden="true">ⓘ</span>
                  <span>{g.examplesSource.label}</span>
                </div>
              )}
              <ul>
                {g.examples.map((ex, i) => <li key={i}><span className="sup-example-dot" /> {ex}</li>)}
              </ul>
            </details>
          )}
        </div>
      )}
    </aside>
  );
}

/* =====================================================================
   Dock — floating action cluster pinned to the viewer corner
   Buttons: Comments [n] · Guidance · Highlight · Take over (supporter only)
   ===================================================================== */
function SupporterDock({ sup, itemId, position = "tr" }) {
  const n = sup.commentCount(itemId);
  const g = sup.guidanceFor(itemId);
  const isSupporter = sup.perspective === "supporter";
  const takenOver = sup.takenOverItemId === itemId;
  return (
    <div className={`sup-dock sup-dock-${position}`} role="toolbar" aria-label="Supporter overlay">
      <button
        className={`sup-dock-btn ${sup.openPanel === "comments" ? "is-active" : ""}`}
        type="button"
        onClick={() => sup.setOpenPanel(sup.openPanel === "comments" ? null : "comments")}
        title="Comment thread"
      >
        <SupIcon name="comment" size={14} />
        <span>{n > 0 ? `Comments · ${n}` : "Comment"}</span>
        {n > 0 && <span className="sup-dock-pip" />}
      </button>
      {isSupporter && (
        <>
          <button
            className={`sup-dock-btn ${(sup.highlights[itemId] || []).length > 0 ? "is-active" : ""}`}
            type="button"
            onClick={() => sup.addHighlight(itemId)}
            title={(sup.highlights[itemId] || []).length > 0 ? "Stop pointing at this question" : "Point at this question for the completer"}
          >
            <SupIcon name="highlight" size={14} />
            <span>{(sup.highlights[itemId] || []).length > 0 ? "Pointing" : "Point at this"}</span>
          </button>
          <button
            className={`sup-dock-btn sup-dock-takeover ${takenOver ? "is-active" : ""}`}
            type="button"
            onClick={() => takenOver ? sup.releaseTakeover() : sup.takeover(itemId)}
            title={takenOver ? "Hand control back to the completer" : "Fill this in for the completer"}
          >
            <SupIcon name="takeover" size={14} />
            <span>{takenOver ? "Hand back" : "Take over"}</span>
          </button>
        </>
      )}
    </div>
  );
}

/* =====================================================================
   CommentPanel — popover anchored under the dock
   ===================================================================== */
function SupporterCommentPanel({ sup, itemId, onClose }) {
  const list = sup.comments[itemId] || [];
  const [draft, setDraft] = supUseState("");
  const inputRef = supUseRef(null);
  supUseEffect(() => {
    const t = setTimeout(() => inputRef.current && inputRef.current.focus(), 80);
    return () => clearTimeout(t);
  }, []);
  const send = () => {
    if (!draft.trim()) return;
    sup.addComment(itemId, draft);
    setDraft("");
  };
  const onKey = (e) => {
    if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) { e.preventDefault(); send(); }
    if (e.key === "Escape") { e.stopPropagation(); onClose(); }
  };

  const isCompleter = sup.perspective === "completer";
  /* Pre-baked openers — one click pre-fills the composer so the user
     never stares at a blank textarea. Tuned per perspective. */
  const quickReplies = isCompleter
    ? ["Why does this matter?", "Show me an example", "I'm stuck — can you help?"]
    : ["Looks good — go with that", "Let me walk you through it", "Want me to fill this in?"];

  const countLabel = list.length === 0 ? "No messages yet"
                   : list.length === 1 ? "1 message"
                   : `${list.length} messages`;

  return (
    <div className="sup-panel sup-panel-comments" role="dialog" aria-label="Comment thread">
      <div className="sup-panel-accent" aria-hidden="true" />
      <header className="sup-panel-head">
        <div className="sup-panel-presence">
          <SupAvatar id={sup.them.avatar} size={36} ring={sup.them.id} />
          <div className="sup-panel-presence-text">
            <div className="sup-panel-presence-name">
              <b>{sup.them.name}</b>
              {sup.them.id === "supporter" && <span className="sup-pill sup-pill-creator">Creator</span>}
            </div>
            <div className="sup-panel-presence-sub">
              <span className="sup-online-dot" aria-hidden="true" />
              <span>Online · {countLabel}</span>
            </div>
          </div>
        </div>
        <button className="sup-icon-btn" type="button" onClick={onClose} aria-label="Close">
          <SupIcon name="close" size={14} />
        </button>
      </header>

      <div className="sup-panel-body">
        {list.length === 0 ? (
          <div className="sup-empty">
            <div className="sup-empty-glyph" aria-hidden="true">
              <SupIcon name="comment" size={22} />
            </div>
            <p className="sup-empty-title">Start the conversation</p>
            <p className="sup-empty-sub">
              {isCompleter
                ? <>Anything you write here goes straight to <b>{sup.them.name}</b>.</>
                : <>Your reply lands in <b>{sup.them.name}</b>'s workshop instantly.</>}
            </p>
            <div className="sup-quick-replies">
              {quickReplies.map((q, i) => (
                <button
                  key={i}
                  type="button"
                  className="sup-chip"
                  onClick={() => { setDraft(q); inputRef.current && inputRef.current.focus(); }}
                >
                  {q}
                </button>
              ))}
            </div>
          </div>
        ) : (
          <ol className="sup-thread">
            {list.map((c, i) => {
              const p = SUPPORTER_PEOPLE[c.who];
              const mine = c.who === sup.perspective;
              return (
                <li key={i} className={`sup-msg ${mine ? "is-mine" : ""} who-${c.who}`}>
                  <SupAvatar id={p.avatar} size={22} ring={c.who} online={false} />
                  <div className="sup-msg-body">
                    <div className="sup-msg-meta">
                      <b style={{ color: p.hue }}>{mine ? "You" : p.name}</b>
                      {c.who === "supporter" && <span className="sup-pill sup-pill-creator">Creator</span>}
                      <span className="sup-msg-ts">{c.ts}</span>
                    </div>
                    <div className="sup-msg-text">{c.text}</div>
                  </div>
                </li>
              );
            })}
          </ol>
        )}
      </div>

      <footer className="sup-panel-foot">
        <SupAvatar id={sup.me.avatar} size={28} ring={sup.perspective} online={false} />
        <textarea
          ref={inputRef}
          className="sup-input"
          rows="2"
          placeholder={isCompleter ? `Ask ${sup.them.name}…` : `Reply to ${sup.them.name}…`}
          value={draft}
          onChange={(e) => setDraft(e.target.value)}
          onKeyDown={onKey}
        />
        <button
          className={`sup-send-btn ${draft.trim() ? "is-ready" : "is-empty"}`}
          type="button"
          onClick={send}
          disabled={!draft.trim()}
          title="Send (⌘↵)"
          aria-label="Send"
        >
          <SupIcon name="send" size={14} />
        </button>
      </footer>
    </div>
  );
}

/* =====================================================================
   CoPilotBar — sticky bottom bar shown only to the supporter
   ===================================================================== */
function SupporterCoPilotBar({ sup, itemId, itemLabel = "current item" }) {
  if (sup.perspective !== "supporter") return null;
  const takenOver = sup.takenOverItemId === itemId;
  const hi = (sup.highlights[itemId] || []).length;
  const n = sup.commentCount(itemId);
  return (
    <aside className="sup-copilot" data-screen-label="Co-pilot bar">
      <div className="sup-copilot-left">
        <SupAvatar id={sup.them.avatar} size={28} ring="completer" />
        <div className="sup-copilot-stack">
          <div className="sup-copilot-eyebrow">Helping <b style={{ color: sup.them.hue }}>{sup.them.name}</b></div>
          <div className="sup-copilot-title">On {itemLabel}{takenOver && <> · <span className="sup-pill sup-pill-warn">You have the keyboard</span></>}</div>
        </div>
      </div>
      <div className="sup-copilot-actions">
        <button className="sup-btn sup-btn-ghost" type="button" onClick={() => sup.setOpenPanel("comments")}>
          <SupIcon name="comment" size={14} /><span>Reply</span>{n > 0 && <span className="sup-pill sup-pill-mute">{n}</span>}
        </button>
        <button className="sup-btn sup-btn-ghost" type="button" onClick={() => sup.addHighlight(itemId)}>
          <SupIcon name="highlight" size={14} /><span>{hi > 0 ? "Pointing" : "Point at this"}</span>
        </button>
        <button
          className={`sup-btn ${takenOver ? "sup-btn-warn" : "sup-btn-primary"}`}
          type="button"
          onClick={() => takenOver ? sup.releaseTakeover() : sup.takeover(itemId)}
        >
          <SupIcon name="takeover" size={14} />
          <span>{takenOver ? "Hand control back" : "Take over"}</span>
        </button>
      </div>
    </aside>
  );
}

/* =====================================================================
   TakeoverPulse — colored ring around the active input + caption
   ===================================================================== */
function SupporterTakeoverPulse({ sup, itemId }) {
  if (sup.takenOverItemId !== itemId) return null;
  const isCompleter = sup.perspective === "completer";
  const helper = SUPPORTER_PEOPLE.supporter;
  return (
    <div className={`sup-takeover-pulse ${isCompleter ? "is-receiving" : "is-driving"}`}>
      <span className="sup-takeover-dot" />
      <SupAvatar id={helper.avatar} size={20} ring="supporter" />
      <span className="sup-takeover-text">
        {isCompleter
          ? <><b style={{ color: helper.hue }}>{helper.name}</b> is filling this in for you</>
          : <>You're typing for <b>{SUPPORTER_PEOPLE.completer.name}</b></>}
      </span>
    </div>
  );
}

/* =====================================================================
   HighlightStrip — shown above question if supporter pointed at it
   ===================================================================== */
function SupporterHighlightStrip({ sup, itemId }) {
  const hi = sup.highlights[itemId] || [];
  if (hi.length === 0) return null;
  const isSelf = hi[0].by === sup.perspective;
  return (
    <div className="sup-hl-strip">
      <SupIcon name="highlight" size={14} />
      <span>
        {isSelf
          ? <>You're pointing at this question for <b>{sup.them.name}</b>.</>
          : <><b>{SUPPORTER_PEOPLE.supporter.name}</b> is pointing at this question.</>}
      </span>
      {sup.perspective === "supporter" && (
        <button className="sup-link" type="button" onClick={() => sup.clearHighlights(itemId)}>Stop pointing</button>
      )}
    </div>
  );
}

/* =====================================================================
   Tweak controls — drop this inside an existing TweaksPanel
   ===================================================================== */
function SupporterPerspectiveTweaks({ sup }) {
  const { TweakSection, TweakRadio, TweakToggle } = window;
  return (
    <>
      <TweakSection label="Perspective">
        <TweakRadio
          options={[
            { value: "completer", label: "Completer" },
            { value: "supporter", label: "Creator (supporter)" },
          ]}
          value={sup.perspective}
          onChange={(v) => sup.setPerspective(v)}
        />
        <div className="gw-tweak-note">Switch between the two seats of a live workshop. Both share the same chrome; the supporter gets extra co-pilot controls overlaid.</div>
      </TweakSection>
      <TweakSection label="Live session">
        <TweakToggle
          label="Session active"
          value={sup.sessionLive}
          onChange={(v) => sup.setSessionLive(v)}
        />
        <div className="gw-tweak-note">When off, the supporter is gone and the completer sees a normal solo workshop.</div>
      </TweakSection>
    </>
  );
}

/* =====================================================================
   useStuckTimer — seconds spent on the current item id.
   Clock resets on itemId change. Components compare vs. their own threshold
   (e.g. show Stuck button after 90s). Pauses while the tab is hidden.
   ===================================================================== */
function useStuckTimer(itemId) {
  const [elapsed, setElapsed] = supUseState(0);
  supUseEffect(() => {
    setElapsed(0);
    if (!itemId) return;
    let handle = null;
    const tick = () => setElapsed(s => s + 1);
    const start = () => { if (!handle) handle = setInterval(tick, 1000); };
    const stop  = () => { if (handle) { clearInterval(handle); handle = null; } };
    const onVis = () => { document.hidden ? stop() : start(); };
    start();
    document.addEventListener("visibilitychange", onVis);
    return () => { stop(); document.removeEventListener("visibilitychange", onVis); };
  }, [itemId]);
  return elapsed;
}

/* =====================================================================
   SupporterStuckButton — per-question "I'm stuck on this".
   Hidden until the user has spent thresholdSec on the item without
   marking it complete. Toggle + optional note. Once stuck, the chapter
   outline highlights red and the live-sessions ranking surfaces it.
   ===================================================================== */
function SupporterStuckButton({ sup, itemId, thresholdSec = 30 }) {
  if (sup.perspective !== "completer") return null;
  const [draftOpen, setDraftOpen] = supUseState(false);
  const [note, setNote] = supUseState("");
  const elapsed = useStuckTimer(itemId);
  const isStuck = sup.stuckItems.has(itemId);
  if (!isStuck && elapsed < thresholdSec) return null;

  if (isStuck) {
    return (
      <button className="sup-stuck sup-stuck-active" type="button" onClick={() => sup.unflagStuck(itemId)} title="Clear the stuck flag">
        <SupIcon name="highlight" size={14} />
        <span>Marked as stuck</span>
        <span className="sup-stuck-clear">Clear</span>
      </button>
    );
  }

  if (draftOpen) {
    const submit = () => { sup.flagStuck(itemId, note); setNote(""); setDraftOpen(false); };
    return (
      <div className="sup-stuck-draft">
        <div className="sup-stuck-draft-head">
          <span><SupIcon name="highlight" size={14} /> What's blocking you?</span>
          <button className="sup-icon-btn" type="button" onClick={() => setDraftOpen(false)} aria-label="Cancel"><SupIcon name="close" size={14} /></button>
        </div>
        <textarea
          className="sup-input"
          rows="2"
          autoFocus
          placeholder="Optional — a quick note for the creator (e.g. 'not sure how to score this')"
          value={note}
          onChange={(e) => setNote(e.target.value)}
          onKeyDown={(e) => { if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) { e.preventDefault(); submit(); } if (e.key === "Escape") { e.preventDefault(); setDraftOpen(false); } }}
        />
        <div className="sup-stuck-draft-foot">
          <button className="sup-btn sup-btn-ghost" type="button" onClick={() => setDraftOpen(false)}>Cancel</button>
          <button className="sup-btn sup-btn-warn" type="button" onClick={submit}>Mark as stuck</button>
        </div>
      </div>
    );
  }

  return (
    <button className="sup-stuck" type="button" onClick={() => setDraftOpen(true)} title={`You've been on this for ${elapsed}s — flag it for the creator`}>
      <SupIcon name="highlight" size={14} />
      <span>I'm stuck on this</span>
    </button>
  );
}

/* =====================================================================
   SupporterAskCreator — explicit summon. Visible only when sessionLive is
   false; once requested, the CTA becomes a "waiting for..." banner.
   ===================================================================== */
function SupporterAskCreator({ sup }) {
  if (sup.perspective !== "completer") return null;
  if (sup.sessionLive) return null;
  const [open, setOpen] = supUseState(false);
  const [note, setNote] = supUseState("");

  if (sup.helpRequested) {
    const mins = Math.max(0, Math.floor((Date.now() - sup.helpRequested.ts) / 60000));
    return (
      <div className="sup-ask-banner">
        <span className="sup-ask-banner-dot" aria-hidden="true" />
        <div className="sup-ask-banner-text">
          <b>Waiting for {SUPPORTER_PEOPLE.supporter.name}</b>
          <span>Sent {mins === 0 ? "just now" : mins + "m ago"}. You'll see a banner here when they join.</span>
        </div>
        <button className="sup-link" type="button" onClick={sup.cancelHelpRequest}>Cancel request</button>
      </div>
    );
  }

  if (open) {
    const send = () => { sup.requestHelp(note); setNote(""); setOpen(false); };
    return (
      <div className="sup-ask-draft">
        <div className="sup-ask-draft-head">
          <span><SupIcon name="send" size={14} /> Ask {SUPPORTER_PEOPLE.supporter.name} to join</span>
          <button className="sup-icon-btn" type="button" onClick={() => setOpen(false)} aria-label="Cancel"><SupIcon name="close" size={14} /></button>
        </div>
        <textarea
          className="sup-input"
          rows="2"
          autoFocus
          placeholder="Optional context — what would help? (e.g. 'walk me through the scoring')"
          value={note}
          onChange={(e) => setNote(e.target.value)}
          onKeyDown={(e) => { if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) { e.preventDefault(); send(); } if (e.key === "Escape") { e.preventDefault(); setOpen(false); } }}
        />
        <div className="sup-ask-draft-foot">
          <button className="sup-btn sup-btn-ghost" type="button" onClick={() => setOpen(false)}>Cancel</button>
          <button className="sup-btn sup-btn-primary" type="button" onClick={send}>Send request</button>
        </div>
      </div>
    );
  }

  return (
    <button className="sup-ask-cta" type="button" onClick={() => setOpen(true)}>
      <SupAvatar id={SUPPORTER_PEOPLE.supporter.avatar} size={22} ring="supporter" online={false} />
      <span><b>Stuck?</b> Ask {SUPPORTER_PEOPLE.supporter.name} to join the workshop.</span>
      <span className="sup-ask-cta-action"><SupIcon name="send" size={12} /> Ask creator</span>
    </button>
  );
}

/* ---------- Export ---------- */
Object.assign(window, {
  useSupporter,
  useStuckTimer,
  SUPPORTER_PEOPLE,
  SUPPORTER_GUIDANCE,
  SupAvatar,
  SupIcon,
  SupporterSessionBanner,
  SupporterGuidanceCard,
  SupporterDock,
  SupporterCommentPanel,
  SupporterCoPilotBar,
  SupporterTakeoverPulse,
  SupporterHighlightStrip,
  SupporterStuckButton,
  SupporterAskCreator,
  SupporterPerspectiveTweaks,
});
