/* ============================================================
   Open Group — ad-hoc games set up on the fly.
   A member picks how many foursomes, drags players (drawn from
   the member list + off-app guests) within/between groups,
   chooses the game + add-ons, and begins. Each foursome opens
   the SAME scorecard used for weekend play (dots, CTP, longest
   drive). Open-group data is stored separately from the weekend
   sheet and feeds the Season tab under its own toggle.
   ============================================================ */

const OG_LETTERS = "ABCDEFGH";
function ogInitials(n) { return (n || "?").trim().split(/\s+/).map((s) => s[0]).join("").slice(0, 2).toUpperCase(); }
function ogStakeLabel(id) {
  return ({ skins: "Skin value", stableford: "Ante (each)", nassau: "Bet / match", "split-sixes": "Point value", wolf: "Point value", "bbb": "Point value" })[id] || "Stake / point ($)";
}

// Build the selectable player pool from account members + the signed-in member.
function ogCandidates(accountMembers, member) {
  const list = [], seen = new Set();
  const push = (o) => {
    const key = (o.email || o.name || "").toLowerCase();
    if (!o.name || seen.has(key)) return;
    seen.add(key); list.push(o);
  };
  (accountMembers || []).forEach((m) => {
    const name = `${m.first_name || ""} ${m.last_name || ""}`.trim() || m.email;
    push({ name, initials: ogInitials(name), hi: (m.handicap_index != null ? m.handicap_index : "—"), ghin: m.ghin || "—", memberId: m.id, tee: m.tee || "White", email: m.email });
  });
  if (member && member.name) push({ name: member.name, initials: member.initials || ogInitials(member.name), hi: (member.handicapIndex != null ? member.handicapIndex : "—"), ghin: member.ghin || "—", memberId: member.id, tee: member.tee || "White", email: member.email });
  return list.sort((a, b) => a.name.localeCompare(b.name));
}

/* -------------------- SETUP OVERLAY -------------------- */
function OpenGroupSetup({ ctx, existing, onClose, onDone }) {
  const Y = window.YCC, G = window.YCC_GAMES;
  const { accountMembers, member, toast } = ctx;
  const candidates = useMemo(() => ogCandidates(accountMembers, member), [accountMembers, member]);
  const candByName = (n) => candidates.find((c) => c.name === n) || { name: n, initials: ogInitials(n), hi: "—", ghin: "—" };

  const todayISO = Y.isoDay(Y.TODAY);
  const editing = !!existing;

  const [step, setStep] = useState(0); // 0 groups · 1 lineup · 2 game
  const [count, setCount] = useState(() => (existing ? Math.max(1, (existing.foursomes || []).length) : 1));
  const [playNow, setPlayNow] = useState(() => (existing ? (existing.playNow !== false) : true));
  const [dateISO, setDateISO] = useState(() => (existing ? existing.dateISO : todayISO));

  // foursomes: [{id, label, players:[{name,initials,hi,ghin,tee,guest,memberId}]}]
  const seed = () => {
    if (existing) return (existing.foursomes || []).map((f, i) => ({ id: f.id, label: f.label || ("Group " + OG_LETTERS[i]), players: (f.players || []).map((p) => ({ ...p })) }));
    const arr = [];
    for (let i = 0; i < count; i++) arr.push({ id: "f" + i, label: "Group " + OG_LETTERS[i], players: [] });
    // default the creator into Group A
    const me = candidates.find((c) => member && c.name === member.name);
    if (me) arr[0].players.push({ ...me });
    return arr;
  };
  const [foursomes, setFoursomes] = useState(seed);

  // keep foursome count in sync when the stepper changes (step 0 only)
  function applyCount(n) {
    n = Math.max(1, Math.min(8, n));
    setCount(n);
    setFoursomes((prev) => {
      const next = prev.slice(0, n).map((f, i) => ({ ...f, label: f.label || ("Group " + OG_LETTERS[i]) }));
      while (next.length < n) next.push({ id: "f" + next.length + "_" + Date.now(), label: "Group " + OG_LETTERS[next.length], players: [] });
      return next;
    });
  }

  // ---- game config ----
  const cfg0 = (existing && existing.config) || {};
  const [gameType, setGameType] = useState(cfg0.gameType || "skins");
  const [groupTee, setGroupTee] = useState(cfg0.groupTee || "White");
  const [scoring, setScoring] = useState(cfg0.scoring || "gross");
  const [carryover, setCarryover] = useState(cfg0.carryover !== false);
  const [stake, setStake] = useState(cfg0.skinValue != null ? cfg0.skinValue : 5);
  const sg0 = (cfg0.sideGames && (cfg0.sideGames.sat || cfg0.sideGames.sun)) || {};
  const [ctp, setCtp] = useState(sg0.ctp !== undefined ? !!sg0.ctp : true);
  const parThrees = G.parThreeHoles();
  const [ctpHoles, setCtpHoles] = useState(() => Array.isArray(sg0.ctpHoles) ? sg0.ctpHoles.slice() : parThrees.slice());
  const toggleCtpHole = (n) => setCtpHoles((prev) => prev.includes(n) ? prev.filter((x) => x !== n) : [...prev, n].sort((a, b) => a - b));
  const [longDrive, setLongDrive] = useState(sg0.longDrive !== undefined ? !!sg0.longDrive : true);
  const [ldHole, setLdHole] = useState(sg0.longDriveHole || 7);
  const [lowMode, setLowMode] = useState(cfg0.lowMode || "day");
  const [rulesOpen, setRulesOpen] = useState(null);

  const placedNames = new Set();
  foursomes.forEach((f) => f.players.forEach((p) => placedNames.add(p.name)));
  const available = candidates.filter((c) => !placedNames.has(c.name));
  const totalPlayers = placedNames.size;

  // ---- drag & drop ----
  const drag = useRef(null); // { fromFid|null, name }
  const [dragName, setDragName] = useState(null);
  const [overFid, setOverFid] = useState(null);

  function startDrag(fromFid, name) { drag.current = { fromFid, name }; setDragName(name); }
  function endDrag() { drag.current = null; setDragName(null); setOverFid(null); }

  function dropInto(toFid, beforeName) {
    const d = drag.current; endDrag();
    if (!d) return;
    setFoursomes((prev) => {
      const next = prev.map((f) => ({ ...f, players: [...f.players] }));
      let pObj;
      if (d.fromFid == null) pObj = { ...candByName(d.name) };
      else { const ff = next.find((f) => f.id === d.fromFid); const idx = ff.players.findIndex((p) => p.name === d.name); if (idx < 0) return prev; pObj = ff.players[idx]; }
      const target = next.find((f) => f.id === toFid);
      if (!target) return prev;
      if (d.fromFid !== toFid && target.players.length >= 4) { toast && toast("That group is full (4 max)"); return prev; }
      if (d.fromFid != null) { const ff = next.find((f) => f.id === d.fromFid); ff.players = ff.players.filter((p) => p.name !== d.name); }
      let insertIdx = target.players.length;
      if (beforeName) { const bi = target.players.findIndex((p) => p.name === beforeName); if (bi >= 0) insertIdx = bi; }
      target.players.splice(insertIdx, 0, pObj);
      return next;
    });
  }
  function dropToPool() {
    const d = drag.current; endDrag();
    if (!d || d.fromFid == null) return;
    setFoursomes((prev) => prev.map((f) => f.id === d.fromFid ? { ...f, players: f.players.filter((p) => p.name !== d.name) } : f));
  }
  function addToFirstOpen(cand) {
    setFoursomes((prev) => {
      const next = prev.map((f) => ({ ...f, players: [...f.players] }));
      const target = next.find((f) => f.players.length < 4);
      if (!target) { toast && toast("All groups are full"); return prev; }
      target.players.push({ ...cand });
      return next;
    });
  }
  function removePlayer(fid, name) {
    setFoursomes((prev) => prev.map((f) => f.id === fid ? { ...f, players: f.players.filter((p) => p.name !== name) } : f));
  }

  // ---- guest add ----
  const [guestOpen, setGuestOpen] = useState(false);
  const [guestName, setGuestName] = useState("");
  const [guestHi, setGuestHi] = useState("");
  const [guestFid, setGuestFid] = useState(null);
  function addGuest() {
    const name = guestName.trim();
    if (!name) return;
    if (placedNames.has(name) || candidates.some((c) => c.name === name)) { toast && toast("That name is already in the list"); return; }
    const fid = guestFid || (foursomes.find((f) => f.players.length < 4) || foursomes[0]).id;
    setFoursomes((prev) => prev.map((f) => f.id === fid
      ? (f.players.length >= 4 ? f : { ...f, players: [...f.players, { name, initials: ogInitials(name), hi: (guestHi === "" ? "—" : Number(guestHi)), ghin: "—", tee: groupTee, guest: true }] })
      : f));
    setGuestName(""); setGuestHi(""); setGuestOpen(false);
  }

  const game = G.gameById(gameType);

  function begin() {
    const live = foursomes.filter((f) => f.players.length > 0).map((f, i) => ({ id: f.id, label: f.label || ("Group " + OG_LETTERS[i]), players: f.players }));
    if (!live.length) { toast && toast("Add at least one player to a group"); return; }
    const sg = { ctp, longDrive, ctpHoles: ctpHoles.slice(), longDriveHole: Number(ldHole) || 7 };
    const config = {
      gameType, groupTee, scoring, carryover, skinValue: Number(stake) || 0,
      splitDays: false, lowMode: (live.length > 1 ? lowMode : "group"),
      sideGames: { sat: { ...sg }, sun: { ...sg } },
    };
    const base = existing || {};
    const session = {
      id: existing ? existing.id : "og-" + Date.now(),
      name: existing && existing.name ? existing.name : (game.name + " · " + Y.shortDate(new Date(dateISO + "T00:00:00"))),
      createdBy: base.createdBy || (member && member.name) || "",
      createdById: base.createdById || (member && member.id) || null,
      createdAt: base.createdAt || new Date().toISOString(),
      dateISO: playNow ? todayISO : dateISO,
      playNow,
      status: base.status || "active",
      config,
      foursomes: live,
      scores: base.scores || {},
      sideResults: base.sideResults || {},
      locks: base.locks || {},
    };
    onDone(session);
  }

  // ---- step chrome ----
  const steps = ["Groups", "Players", "Game"];
  const canNext = step === 0 ? count >= 1 : step === 1 ? totalPlayers >= 1 : true;

  return (
    <Modal onClose={onClose} wide>
      <ModalHead title={editing ? "Edit Open Group" : "New Open Group"} sub="Set up a game on the fly — pick groups, players, and a format, then play." onClose={onClose} />

      {/* step rail */}
      <div style={{ display: "flex", gap: 8, padding: "14px 24px 0", flexWrap: "wrap" }}>
        {steps.map((s, i) => (
          <button key={s} onClick={() => (i < step || canNext || i < step) && setStep(i)} style={{
            display: "flex", alignItems: "center", gap: 8, padding: "6px 12px", borderRadius: 999, fontSize: 12.5, fontWeight: 700,
            border: "1px solid " + (i === step ? "var(--brand)" : "var(--line-strong)"),
            background: i === step ? "var(--brand)" : (i < step ? "var(--brand-soft)" : "var(--surface)"),
            color: i === step ? "var(--on-brand)" : (i < step ? "var(--brand)" : "var(--ink-soft)"), cursor: "pointer",
          }}>
            <span style={{ width: 18, height: 18, borderRadius: "50%", display: "grid", placeItems: "center", fontSize: 11, background: i === step ? "var(--accent)" : (i < step ? "var(--brand)" : "var(--surface-2)"), color: i === step ? "#1b1404" : (i < step ? "#fff" : "var(--ink-soft)") }}>
              {i < step ? <Icon name="check" size={11} /> : i + 1}
            </span>
            {s}
          </button>
        ))}
      </div>

      <div style={{ padding: "18px 24px 6px", maxHeight: "60vh", overflowY: "auto" }}>
        {/* STEP 0 — group count + when */}
        {step === 0 && (
          <div className="stack" style={{ gap: 20 }}>
            <div>
              <div className="eyebrow" style={{ marginBottom: 10 }}>How many groups?</div>
              <p className="muted" style={{ fontSize: 13.5, margin: "0 0 14px", lineHeight: 1.5 }}>Each group is a foursome — up to 4 players. You can drag players between groups in the next step.</p>
              <div style={{ display: "flex", alignItems: "center", gap: 16 }}>
                <button className="btn btn-ghost" onClick={() => applyCount(count - 1)} disabled={count <= 1} style={{ width: 46, height: 46, padding: 0, borderRadius: 12 }}><Icon name="minus" size={18} /></button>
                <div style={{ textAlign: "center", minWidth: 90 }}>
                  <div className="serif" style={{ fontSize: 48, fontWeight: 700, lineHeight: 1, color: "var(--brand)" }}>{count}</div>
                  <div className="faint" style={{ fontSize: 12, marginTop: 4 }}>group{count > 1 ? "s" : ""} · up to {count * 4} players</div>
                </div>
                <button className="btn btn-ghost" onClick={() => applyCount(count + 1)} disabled={count >= 8} style={{ width: 46, height: 46, padding: 0, borderRadius: 12 }}><Icon name="plus" size={18} /></button>
              </div>
            </div>
            <div className="hairline"></div>
            <div>
              <div className="eyebrow" style={{ marginBottom: 10 }}>When</div>
              <div style={{ display: "flex", gap: 10, flexWrap: "wrap", alignItems: "center" }}>
                <button onClick={() => setPlayNow(true)} style={ogSegStyle(playNow)}><Icon name="flag" size={14} /> Play now</button>
                <button onClick={() => setPlayNow(false)} style={ogSegStyle(!playNow)}><Icon name="calendar" size={14} /> Pick a date</button>
                {!playNow && (
                  <input type="date" value={dateISO} min={todayISO} onChange={(e) => setDateISO(e.target.value)} className="input" style={{ width: "auto", padding: "9px 12px" }} />
                )}
              </div>
              <p className="faint" style={{ fontSize: 12, margin: "10px 0 0", lineHeight: 1.5 }}>{playNow ? "The scorecard opens immediately so you can enter scores as you play." : "The scorecard opens on the chosen day — set up the lineup now."}</p>
            </div>
          </div>
        )}

        {/* STEP 1 — lineup with drag & drop */}
        {step === 1 && (
          <div className="stack" style={{ gap: 16 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 12.5, color: "var(--ink-soft)" }}>
              <Icon name="swap" size={15} style={{ color: "var(--accent-deep)" }} /> Drag a player into a group, between groups, or reorder within one. Tap to add to the first open seat.
            </div>

            {/* available pool */}
            <div onDragOver={(e) => { if (drag.current) e.preventDefault(); }} onDrop={(e) => { e.preventDefault(); dropToPool(); }}
              style={{ border: "1px dashed var(--line-strong)", borderRadius: 12, padding: 12, background: "var(--surface-2)" }}>
              <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 10, marginBottom: 10, flexWrap: "wrap" }}>
                <span className="eyebrow">Members ({available.length})</span>
                <button className="btn btn-ghost btn-sm" onClick={() => setGuestOpen((v) => !v)}><Icon name="plus" size={13} /> Off-app guest</button>
              </div>
              {guestOpen && (
                <div style={{ display: "flex", gap: 8, flexWrap: "wrap", alignItems: "center", marginBottom: 12, padding: "10px 12px", background: "var(--surface)", borderRadius: 10, border: "1px solid var(--line)" }}>
                  <input className="input" placeholder="Guest name" value={guestName} onChange={(e) => setGuestName(e.target.value)} style={{ flex: "1 1 150px", width: "auto", padding: "9px 12px" }} />
                  <input className="input" placeholder="HI" value={guestHi} inputMode="decimal" onChange={(e) => setGuestHi(e.target.value.replace(/[^0-9.\-]/g, ""))} style={{ width: 70, padding: "9px 12px" }} />
                  <select className="input" value={guestFid || ""} onChange={(e) => setGuestFid(e.target.value)} style={{ width: "auto", padding: "9px 12px" }}>
                    {foursomes.map((f, i) => <option key={f.id} value={f.id}>{f.label}</option>)}
                  </select>
                  <button className="btn btn-primary btn-sm" onClick={addGuest}>Add</button>
                </div>
              )}
              <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
                {available.length === 0 && <span className="faint" style={{ fontSize: 12.5 }}>Everyone is assigned to a group.</span>}
                {available.map((c) => (
                  <button key={c.name} draggable onDragStart={() => startDrag(null, c.name)} onDragEnd={endDrag} onClick={() => addToFirstOpen(c)}
                    title={`HI ${c.hi} · GHIN ${c.ghin} — tap or drag to add`}
                    style={{ display: "inline-flex", alignItems: "center", gap: 8, padding: "5px 12px 5px 5px", borderRadius: 999, border: "1px solid var(--line-strong)", background: "var(--surface)", cursor: "grab", opacity: dragName === c.name ? .4 : 1 }}>
                    <Avatar initials={c.initials} size={22} tone="brand" />
                    <span style={{ fontSize: 12.5, fontWeight: 600, whiteSpace: "nowrap" }}>{c.name}</span>
                    <span className="faint" style={{ fontSize: 11 }}>{c.hi}</span>
                  </button>
                ))}
              </div>
            </div>

            {/* group columns */}
            <div style={{ display: "grid", gridTemplateColumns: foursomes.length > 1 ? "repeat(auto-fit, minmax(220px, 1fr))" : "1fr", gap: 12 }}>
              {foursomes.map((f, i) => (
                <div key={f.id}
                  onDragOver={(e) => { if (drag.current) { e.preventDefault(); setOverFid(f.id); } }}
                  onDragLeave={() => setOverFid((p) => p === f.id ? null : p)}
                  onDrop={(e) => { e.preventDefault(); dropInto(f.id, null); }}
                  style={{ border: "1px solid " + (overFid === f.id ? "var(--accent)" : "var(--line)"), borderRadius: 12, background: overFid === f.id ? "color-mix(in srgb,var(--accent) 8%,var(--surface))" : "var(--surface)", overflow: "hidden", transition: "border-color .12s, background .12s" }}>
                  <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8, padding: "9px 12px", background: "var(--brand)", color: "var(--on-brand)" }}>
                    <span style={{ fontWeight: 700, fontSize: 13 }}>{f.label}</span>
                    <span style={{ fontSize: 11, color: "rgba(244,241,230,.7)" }}>{f.players.length}/4</span>
                  </div>
                  <div className="stack" style={{ gap: 7, padding: 10, minHeight: 64 }}>
                    {f.players.length === 0 && <div className="faint" style={{ fontSize: 12, textAlign: "center", padding: "12px 0" }}>Drop players here</div>}
                    {f.players.map((p) => (
                      <div key={p.name} draggable onDragStart={() => startDrag(f.id, p.name)} onDragEnd={endDrag}
                        onDragOver={(e) => { if (drag.current) e.preventDefault(); }} onDrop={(e) => { e.preventDefault(); e.stopPropagation(); dropInto(f.id, p.name); }}
                        style={{ display: "flex", alignItems: "center", gap: 9, padding: "7px 9px", borderRadius: 9, background: "var(--surface-2)", border: "1px solid var(--line)", cursor: "grab", opacity: dragName === p.name ? .4 : 1 }}>
                        <Icon name="menu" size={13} style={{ color: "var(--ink-faint)", flex: "0 0 auto" }} />
                        <Avatar initials={p.initials} size={26} tone={member && p.name === member.name ? "accent" : "brand"} />
                        <div style={{ minWidth: 0, flex: 1 }}>
                          <div style={{ fontSize: 12.5, fontWeight: 600, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{p.name}{p.guest ? "" : ""}</div>
                          <div className="faint" style={{ fontSize: 10.5 }}>HI {p.hi}{p.guest ? " · guest" : ""}</div>
                        </div>
                        <button onClick={() => removePlayer(f.id, p.name)} title="Remove" style={{ width: 22, height: 22, borderRadius: "50%", display: "grid", placeItems: "center", color: "var(--danger)", border: "1px solid color-mix(in srgb,var(--danger) 28%,transparent)", flex: "0 0 auto" }}><Icon name="x" size={11} /></button>
                      </div>
                    ))}
                  </div>
                </div>
              ))}
            </div>
          </div>
        )}

        {/* STEP 2 — game + add-ons */}
        {step === 2 && (
          <div className="stack" style={{ gap: 18 }}>
            <div>
              <div className="eyebrow" style={{ marginBottom: 10 }}>Game format</div>
              <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(150px,1fr))", gap: 8 }}>
                {G.GAME_LIBRARY.map((g) => {
                  const on = gameType === g.id;
                  return (
                    <button key={g.id} onClick={() => setGameType(g.id)} style={{
                      textAlign: "left", padding: "11px 12px", borderRadius: 11, border: "1px solid " + (on ? "var(--accent)" : "var(--line)"),
                      background: on ? "color-mix(in srgb,var(--accent) 10%,var(--surface))" : "var(--surface)", cursor: "pointer",
                    }}>
                      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 6 }}>
                        <span className="serif" style={{ fontSize: 16, fontWeight: 700 }}>{g.name}</span>
                        {on && <Icon name="check" size={14} style={{ color: "var(--accent-deep)" }} />}
                      </div>
                      <div className="faint" style={{ fontSize: 10.5, fontWeight: 600, textTransform: "uppercase", letterSpacing: ".02em", marginTop: 2 }}>{g.best}</div>
                    </button>
                  );
                })}
              </div>
              <button onClick={() => setRulesOpen(game)} style={{ background: "none", padding: "8px 0 0", color: "var(--accent-deep)", fontWeight: 700, fontSize: 12.5, display: "inline-flex", alignItems: "center", gap: 5 }}>
                <Icon name="info" size={13} /> {game.name} rules &amp; scoring
              </button>
            </div>

            <div className="hairline"></div>

            {/* tees + scoring + stake */}
            <div style={{ display: "flex", gap: 16, flexWrap: "wrap", alignItems: "center" }}>
              <div>
                <div className="eyebrow" style={{ marginBottom: 7 }}>Tees</div>
                <TeeSelect value={groupTee} onChange={setGroupTee} />
              </div>
              <div>
                <div className="eyebrow" style={{ marginBottom: 7 }}>Scoring</div>
                <div style={{ display: "inline-flex", background: "var(--surface-2)", borderRadius: 9, padding: 3, border: "1px solid var(--line)" }}>
                  {[["gross", "Gross"], ["net", "Net"]].map(([v, l]) => (
                    <button key={v} onClick={() => setScoring(v)} style={ogPillStyle(scoring === v)}>{l}</button>
                  ))}
                </div>
              </div>
              <div>
                <div className="eyebrow" style={{ marginBottom: 7 }}>{ogStakeLabel(gameType)}</div>
                <div style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
                  <span style={{ fontWeight: 700, color: "var(--ink-soft)" }}>$</span>
                  <input className="input" value={stake} inputMode="numeric" onChange={(e) => setStake(e.target.value.replace(/[^0-9]/g, ""))} style={{ width: 72, padding: "9px 11px" }} />
                </div>
              </div>
              {gameType === "skins" && (
                <div>
                  <div className="eyebrow" style={{ marginBottom: 7 }}>Carryover</div>
                  <button onClick={() => setCarryover((v) => !v)} style={ogPillStyle(carryover, true)}>{carryover ? "On — ties carry" : "Off"}</button>
                </div>
              )}
              {foursomes.filter((f) => f.players.length > 0).length > 1 && (
                <div>
                  <div className="eyebrow" style={{ marginBottom: 7 }} title="Whose handicap the dots are taken off">Strokes off</div>
                  <div style={{ display: "inline-flex", background: "var(--surface-2)", borderRadius: 9, padding: 3, border: "1px solid var(--line)" }}>
                    {[["day", "Field low"], ["group", "Group low"]].map(([v, l]) => (
                      <button key={v} onClick={() => setLowMode(v)} style={ogPillStyle(lowMode === v)}>{l}</button>
                    ))}
                  </div>
                </div>
              )}
            </div>

            <div className="hairline"></div>

            {/* add-on games */}
            <div>
              <div className="eyebrow" style={{ marginBottom: 10 }}>Add-on games</div>
              <div className="stack" style={{ gap: 8 }}>
                <div>
                  <button onClick={() => setCtp((v) => !v)} style={ogToggleRow(ctp)}>
                    <span style={{ display: "flex", alignItems: "center", gap: 10 }}>
                      <Icon name="pin" size={16} style={{ color: ctp ? "var(--accent-deep)" : "var(--ink-faint)" }} />
                      <span><span style={{ fontWeight: 700, fontSize: 13.5 }}>Closest to the Pin</span><span className="faint" style={{ fontSize: 11.5, display: "block" }}>{ctp ? (ctpHoles.length === parThrees.length ? "Marked on every par 3" : ctpHoles.length === 0 ? "Pick at least one par 3 below" : "On " + ctpHoles.length + " of " + parThrees.length + " par 3s") : "Mark the par 3s to play"}</span></span>
                    </span>
                    <span style={ogCheck(ctp)}>{ctp && <Icon name="check" size={13} />}</span>
                  </button>
                  {ctp && (
                    <div style={{ display: "flex", gap: 8, flexWrap: "wrap", padding: "10px 4px 2px 44px" }}>
                      {parThrees.map((n) => {
                        const on = ctpHoles.includes(n);
                        const hole = Y.HOLES.find((h) => h.n === n);
                        return (
                          <button key={n} onClick={() => toggleCtpHole(n)} style={{ display: "inline-flex", alignItems: "center", gap: 8, padding: "7px 12px", borderRadius: 9, border: "1px solid " + (on ? "var(--accent)" : "var(--line-strong)"), background: on ? "color-mix(in srgb,var(--accent) 12%,var(--surface))" : "var(--surface)", cursor: "pointer" }}>
                            <span style={{ width: 18, height: 18, borderRadius: 5, flex: "0 0 auto", display: "grid", placeItems: "center", background: on ? "var(--accent)" : "var(--surface-2)", border: "1px solid " + (on ? "var(--accent)" : "var(--line-strong)"), color: "#1b1404" }}>{on && <Icon name="check" size={12} />}</span>
                            <span style={{ fontSize: 12.5, fontWeight: 700 }}>Hole {n}</span>
                            <span className="faint" style={{ fontSize: 11 }}>par {hole ? hole.par : 3} · {Y.yardsForTee(groupTee, n - 1)} yds</span>
                          </button>
                        );
                      })}
                    </div>
                  )}
                </div>
                <button onClick={() => setLongDrive((v) => !v)} style={ogToggleRow(longDrive)}>
                  <span style={{ display: "flex", alignItems: "center", gap: 10 }}>
                    <Icon name="flag" size={16} style={{ color: longDrive ? "var(--ok)" : "var(--ink-faint)" }} />
                    <span><span style={{ fontWeight: 700, fontSize: 13.5 }}>Longest Drive</span><span className="faint" style={{ fontSize: 11.5, display: "block" }}>One designated hole</span></span>
                  </span>
                  <span style={{ display: "flex", alignItems: "center", gap: 10 }}>
                    {longDrive && (
                      <select value={ldHole} onClick={(e) => e.stopPropagation()} onChange={(e) => setLdHole(Number(e.target.value))} style={{ border: "1px solid var(--line-strong)", borderRadius: 8, padding: "5px 8px", background: "var(--surface)", fontFamily: "var(--font-body)", fontSize: 12.5, fontWeight: 600 }}>
                        {Y.HOLES.map((h) => <option key={h.n} value={h.n}>Hole {h.n} · par {h.par}</option>)}
                      </select>
                    )}
                    <span style={ogCheck(longDrive)}>{longDrive && <Icon name="check" size={13} />}</span>
                  </span>
                </button>
              </div>
            </div>

            {/* summary */}
            <div style={{ display: "flex", gap: 14, flexWrap: "wrap", padding: "12px 14px", background: "var(--brand-soft)", borderRadius: 11, fontSize: 12.5, color: "var(--brand)" }}>
              <span><strong>{foursomes.filter((f) => f.players.length > 0).length}</strong> group{foursomes.filter((f) => f.players.length > 0).length > 1 ? "s" : ""}</span>
              <span><strong>{totalPlayers}</strong> player{totalPlayers > 1 ? "s" : ""}</span>
              <span><strong>{game.name}</strong> · {scoring}</span>
              <span><strong>{groupTee}</strong> tees</span>
              {(ctp || longDrive) && <span>{[ctp && "CTP", longDrive && "Long drive"].filter(Boolean).join(" · ")}</span>}
            </div>
          </div>
        )}
      </div>

      {/* footer nav */}
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, padding: "14px 24px", borderTop: "1px solid var(--line)" }}>
        <button className="btn btn-ghost" onClick={step === 0 ? onClose : () => setStep(step - 1)}>{step === 0 ? "Cancel" : <><Icon name="chevL" size={15} /> Back</>}</button>
        {step < 2
          ? <button className="btn btn-primary" disabled={!canNext} onClick={() => setStep(step + 1)} title={!canNext ? (step === 1 ? "Add at least one player" : "") : ""}>Next <Icon name="chevR" size={15} /></button>
          : <button className="btn btn-accent" onClick={begin}><Icon name="flag" size={15} /> {editing ? "Save changes" : (playNow ? "Begin — open scorecards" : "Create Open Group")}</button>}
      </div>

      {rulesOpen && <G.GameRulesModal game={rulesOpen} onClose={() => setRulesOpen(null)} />}
    </Modal>
  );
}

function ogSegStyle(on) {
  return {
    display: "inline-flex", alignItems: "center", gap: 7, padding: "9px 15px", borderRadius: 10, fontSize: 13, fontWeight: 700,
    border: "1px solid " + (on ? "var(--brand)" : "var(--line-strong)"), background: on ? "var(--brand)" : "var(--surface)",
    color: on ? "var(--on-brand)" : "var(--ink)", cursor: "pointer",
  };
}
function ogPillStyle(on, standalone) {
  if (standalone) return { padding: "8px 13px", borderRadius: 9, fontSize: 12.5, fontWeight: 700, border: "1px solid " + (on ? "var(--brand)" : "var(--line-strong)"), background: on ? "var(--brand-soft)" : "var(--surface)", color: on ? "var(--brand)" : "var(--ink-soft)", cursor: "pointer" };
  return { padding: "6px 13px", borderRadius: 7, fontSize: 12.5, fontWeight: 600, background: on ? "var(--surface)" : "transparent", color: on ? "var(--brand)" : "var(--ink-soft)", boxShadow: on ? "var(--shadow-sm)" : "none", cursor: "pointer" };
}
function ogToggleRow(on) {
  return { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, width: "100%", textAlign: "left", padding: "11px 14px", borderRadius: 11, border: "1px solid " + (on ? "var(--brand)" : "var(--line)"), background: on ? "var(--brand-soft)" : "var(--surface)", cursor: "pointer" };
}
function ogCheck(on) {
  return { width: 22, height: 22, borderRadius: 6, flex: "0 0 auto", display: "grid", placeItems: "center", background: on ? "var(--brand)" : "var(--surface-2)", color: "#fff", border: "1px solid " + (on ? "var(--brand)" : "var(--line-strong)") };
}

/* -------------------- OPEN GROUP SCREEN -------------------- */
function OpenGroupScreen({ ctx }) {
  const Y = window.YCC, G = window.YCC_GAMES;
  const { openDoc, member, role, toast } = ctx;
  const sessions = (openDoc && openDoc.sessions) || [];
  const isManager = role === "manager";
  const todayISO = Y.isoDay(Y.TODAY);

  const [setupOpen, setSetupOpen] = useState(false);
  const [editId, setEditId] = useState(null);
  const [selId, setSelId] = useState(null);
  const [rulesOpen, setRulesOpen] = useState(null);
  const [renaming, setRenaming] = useState(false);
  const [nameDraft, setNameDraft] = useState("");

  // default selection → newest active, else newest
  const sorted = [...sessions].sort((a, b) => (b.createdAt || "").localeCompare(a.createdAt || ""));
  const selected = sorted.find((s) => s.id === selId) || sorted.find((s) => s.status !== "done") || sorted[0] || null;
  useEffect(() => { if (selected && selected.id !== selId) setSelId(selected.id); }, [selected && selected.id]);

  const editSession = editId ? sessions.find((s) => s.id === editId) : null;

  function onSetupDone(session) {
    if (editId) ctx.updateOpenSession(session.id, session);
    else { ctx.createOpenSession(session); setSelId(session.id); }
    setSetupOpen(false); setEditId(null);
  }

  return (
    <div className="page fade-in">
      <div className="page-head" style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-end", flexWrap: "wrap", gap: 16 }}>
        <div>
          <div className="eyebrow">DIV Tour · Open Play</div>
          <h1 style={{ marginTop: 6 }}>Open Group</h1>
          <p>Set up a game on the fly — any members, any format. Scorecards work exactly like weekend play, and results are kept separate from the weekend group.</p>
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <SaveBadge status={ctx.saveStatus} />
          <button className="btn btn-accent" onClick={() => { setEditId(null); setSetupOpen(true); }}><Icon name="plus" size={16} /> New Open Group</button>
        </div>
      </div>

      {sessions.length === 0 ? (
        <div className="card" style={{ overflow: "hidden", border: "1px solid var(--accent)" }}>
          <div style={{ padding: "30px var(--card-pad)", textAlign: "center" }}>
            <div style={{ width: 56, height: 56, borderRadius: "50%", margin: "0 auto", display: "grid", placeItems: "center", background: "var(--brand-soft)", color: "var(--brand)" }}><Icon name="users" size={26} /></div>
            <h3 className="serif" style={{ fontSize: 25, marginTop: 14 }}>Start an Open Group</h3>
            <p className="muted" style={{ fontSize: 14.5, margin: "8px auto 20px", maxWidth: 540, lineHeight: 1.55 }}>Pick how many groups, drag your players in from the member list, choose a game and add-ons, and tee off. Every group gets the full Yardley scorecard with stroke dots, closest-to-the-pin and longest drive.</p>
            <button className="btn btn-primary" onClick={() => { setEditId(null); setSetupOpen(true); }}><Icon name="plus" size={16} /> New Open Group</button>
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px,1fr))", gap: 1, background: "var(--line)", borderTop: "1px solid var(--line)" }}>
            {[["users", "Pick groups & players", "1–8 foursomes, drag to arrange"], ["trophy", "Choose the game", "Any format + CTP / long drive"], ["flag", "Play now", "Live scorecards, saved separately"]].map(([ic, t, s]) => (
              <div key={t} style={{ background: "var(--surface)", padding: "16px 18px" }}>
                <Icon name={ic} size={17} style={{ color: "var(--accent-deep)" }} />
                <div style={{ fontWeight: 700, fontSize: 13.5, marginTop: 7 }}>{t}</div>
                <div className="faint" style={{ fontSize: 12, marginTop: 2 }}>{s}</div>
              </div>
            ))}
          </div>
        </div>
      ) : (
        <>
          {/* session switcher */}
          <div style={{ display: "flex", gap: 10, overflowX: "auto", paddingBottom: 6, marginBottom: 20 }}>
            {sorted.map((s) => {
              const on = selected && s.id === selected.id;
              const gm = G.gameById((s.config || {}).gameType);
              const players = (s.foursomes || []).reduce((n, f) => n + f.players.length, 0);
              const done = s.status === "done";
              return (
                <button key={s.id} onClick={() => setSelId(s.id)} style={{
                  flex: "0 0 auto", textAlign: "left", padding: "11px 15px", borderRadius: 12, minWidth: 180,
                  border: "1px solid " + (on ? "var(--brand)" : "var(--line)"), background: on ? "var(--brand)" : "var(--surface)",
                  color: on ? "var(--on-brand)" : "var(--ink)", cursor: "pointer",
                }}>
                  <div style={{ display: "flex", alignItems: "center", gap: 7, marginBottom: 4 }}>
                    <span style={{ width: 7, height: 7, borderRadius: "50%", background: done ? "var(--ink-faint)" : "var(--ok)", display: "inline-block" }}></span>
                    <span style={{ fontSize: 11, fontWeight: 700, letterSpacing: ".04em", textTransform: "uppercase", opacity: on ? .8 : .6 }}>{done ? "Final" : "Active"}</span>
                  </div>
                  <div style={{ fontWeight: 700, fontSize: 14, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", maxWidth: 200 }}>{gm.name}</div>
                  <div style={{ fontSize: 11.5, opacity: .75, marginTop: 2, whiteSpace: "nowrap" }}>{Y.shortDate(new Date(s.dateISO + "T00:00:00"))} · {players} player{players === 1 ? "" : "s"}</div>
                </button>
              );
            })}
          </div>

          {selected && <OpenSessionView key={selected.id} ctx={ctx} session={selected}
            onRules={(g) => setRulesOpen(g)}
            onEdit={() => { setEditId(selected.id); setSetupOpen(true); }} />}
        </>
      )}

      {setupOpen && <OpenGroupSetup ctx={ctx} existing={editSession} onClose={() => { setSetupOpen(false); setEditId(null); }} onDone={onSetupDone} />}
      {rulesOpen && <G.GameRulesModal game={rulesOpen} onClose={() => setRulesOpen(null)} />}
    </div>
  );
}

/* -------- One open session: header + a scorecard per foursome -------- */
function OpenSessionView({ ctx, session, onRules, onEdit }) {
  const Y = window.YCC, G = window.YCC_GAMES;
  const { member, role, toast } = ctx;
  const isManager = role === "manager";
  const todayISO = Y.isoDay(Y.TODAY);
  const cfg = session.config || {};
  const game = G.gameById(cfg.gameType);
  const tee = cfg.groupTee || "White";
  const canManage = isManager || (session.createdById && member && session.createdById === member.id) || (session.createdBy && member && session.createdBy === member.name);

  const [renaming, setRenaming] = useState(false);
  const [nameDraft, setNameDraft] = useState(session.name || "");
  const [confirmDel, setConfirmDel] = useState(false);

  // field low across all foursomes — each player's course handicap off THEIR OWN tee
  const chs = [];
  (session.foursomes || []).forEach((f) => f.players.forEach((p) => { const c = Y.courseHcpOf(p, tee); if (c != null) chs.push(c); }));
  const fieldLow = chs.length ? Math.min(...chs) : null;
  const lowMode = cfg.lowMode || ((session.foursomes || []).length > 1 ? "day" : "group");

  // build a scores/sideResults map keyed dayISO|foursomeId so GroupScorecard reads them
  const sessScores = {}, sessSide = {};
  (session.foursomes || []).forEach((f) => {
    sessScores[session.dateISO + "|" + f.id] = (session.scores || {})[f.id] || {};
    sessSide[session.dateISO + "|" + f.id] = (session.sideResults || {})[f.id] || {};
  });
  const setScore = (dayISO, slotId, name, hIdx, val) => ctx.setOpenScore(session.id, slotId, name, hIdx, val);
  const setSideResult = (dayISO, slotId, kind, holeIdx, name) => ctx.setOpenSideResult(session.id, slotId, kind, holeIdx, name);
  const setScoreLock = (dayISO, slotId, locked, by) => ctx.setOpenScoreLock(session.id, slotId, locked, by);

  const groups = (session.foursomes || []).map((f, i) => ({
    dayISO: session.dateISO, dayLabel: "Open Group", slotId: f.id,
    time: f.label || ("Group " + OG_LETTERS[i]),
    slot: { id: f.id, time: f.label, players: f.players },
  }));

  const done = session.status === "done";

  function saveName() {
    const n = nameDraft.trim();
    if (n) ctx.updateOpenSession(session.id, { name: n });
    setRenaming(false);
  }

  return (
    <div>
      {/* session header */}
      <div className="card" style={{ overflow: "hidden", marginBottom: 22 }}>
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 14, padding: "16px var(--card-pad)", background: "var(--brand-deep)", color: "var(--on-brand)", flexWrap: "wrap" }}>
          <div style={{ minWidth: 0 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
              {renaming ? (
                <span style={{ display: "inline-flex", gap: 6 }}>
                  <input className="input" value={nameDraft} autoFocus onChange={(e) => setNameDraft(e.target.value)} onKeyDown={(e) => e.key === "Enter" && saveName()} style={{ padding: "6px 10px", width: 240, color: "var(--ink)" }} />
                  <button className="btn btn-accent btn-sm" onClick={saveName}>Save</button>
                </span>
              ) : (
                <span className="serif" style={{ fontSize: 24, fontWeight: 700 }}>{session.name}</span>
              )}
              <span className="pill" style={{ background: done ? "rgba(255,255,255,.14)" : "var(--accent)", color: done ? "var(--on-brand)" : "#1b1404" }}>{done ? "Final" : "Active"}</span>
            </div>
            <div style={{ fontSize: 12, color: "rgba(244,241,230,.7)", marginTop: 4 }}>
              {Y.fmtDate(new Date(session.dateISO + "T00:00:00"))} · {game.name} · {tee.toUpperCase()} TEES · set up by {session.createdBy ? session.createdBy.split(" ")[0] : "a member"}
            </div>
          </div>
          <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
            <button className="btn btn-accent btn-sm" onClick={() => onRules(game)}><Icon name="info" size={13} /> Rules</button>
            {canManage && !done && <button className="btn btn-ghost btn-sm" style={{ color: "var(--on-brand)", borderColor: "rgba(255,255,255,.25)" }} onClick={onEdit}><Icon name="edit" size={13} /> Edit lineup</button>}
            {canManage && !renaming && <button className="btn btn-ghost btn-sm" style={{ color: "var(--on-brand)", borderColor: "rgba(255,255,255,.25)" }} onClick={() => { setNameDraft(session.name || ""); setRenaming(true); }} title="Rename"><Icon name="edit" size={13} /></button>}
            {canManage && (
              <button className="btn btn-ghost btn-sm" style={{ color: "var(--on-brand)", borderColor: "rgba(255,255,255,.25)" }} onClick={() => ctx.updateOpenSession(session.id, { status: done ? "active" : "done" })}>
                {done ? <><Icon name="swap" size={13} /> Reopen</> : <><Icon name="check" size={13} /> End game</>}
              </button>
            )}
            {canManage && (
              confirmDel
                ? <button className="btn btn-danger btn-sm" style={{ background: "var(--danger)", color: "#fff" }} onClick={() => ctx.deleteOpenSession(session.id)}><Icon name="trash" size={13} /> Delete — sure?</button>
                : <button className="btn btn-ghost btn-sm" style={{ color: "var(--on-brand)", borderColor: "rgba(255,255,255,.25)" }} onClick={() => { setConfirmDel(true); setTimeout(() => setConfirmDel(false), 3500); }}><Icon name="trash" size={13} /></button>
            )}
          </div>
        </div>
      </div>

      {/* one scorecard per foursome — reuses the weekend GroupScorecard */}
      <div className="stack" style={{ gap: 26 }}>
        {groups.map((g) => (
          <GroupScorecard key={g.slotId} group={g} gameConfig={cfg}
            scoring={cfg.scoring} carryover={cfg.carryover} stake={cfg.skinValue} groupTee={tee}
            isManager={canManage} member={member} scores={sessScores} setScore={setScore}
            sideResults={sessSide} setSideResult={setSideResult}
            todayISO={todayISO} lowMode={lowMode} dayLowCH={fieldLow}
            confirmedNames={ctx.confirmedNames} onRules={onRules}
            scoreLock={(session.locks || {})[g.slotId]} setScoreLock={setScoreLock} />
        ))}
      </div>
    </div>
  );
}

/* -------- Compact session history for the Season tab (Open Group toggle) -------- */
function OpenSessionHistory({ ctx }) {
  const Y = window.YCC, G = window.YCC_GAMES, ENG = window.YCC_SCORING;
  const sessions = ((ctx.openDoc && ctx.openDoc.sessions) || []).slice().sort((a, b) => (b.dateISO || "").localeCompare(a.dateISO || ""));
  const me = ctx.member && ctx.member.name;
  const ini = (n) => (n || "?").trim().split(/\s+/).map((s) => s[0]).join("").slice(0, 2).toUpperCase();

  if (!sessions.length) {
    return (
      <div className="card" style={{ overflow: "hidden", marginTop: 22 }}>
        <div className="card-head"><h3>Open Group sessions</h3><Icon name="users" size={20} style={{ color: "var(--accent-deep)" }} /></div>
        <div style={{ textAlign: "center", padding: "30px 10px", color: "var(--ink-soft)" }}>
          <Icon name="users" size={26} style={{ color: "var(--ink-faint)" }} />
          <p style={{ fontSize: 14, marginTop: 10, fontWeight: 600 }}>No Open Group games yet</p>
          <p className="faint" style={{ fontSize: 12.5, marginTop: 4 }}>Start one from the Open Group tab — its stats land here, separate from weekend play.</p>
        </div>
      </div>
    );
  }

  return (
    <div className="card" style={{ overflow: "hidden", marginTop: 22 }}>
      <div className="card-head"><div><h3>Open Group sessions</h3><div className="muted" style={{ fontSize: 12.5, marginTop: 2 }}>Every ad-hoc game played, newest first.</div></div><Icon name="users" size={20} style={{ color: "var(--accent-deep)" }} /></div>
      <div className="stack" style={{ gap: 14, padding: "16px var(--card-pad)" }}>
        {sessions.map((s) => {
          const gm = G.gameById((s.config || {}).gameType);
          const stats = ENG.openGroupStats ? ENG.openGroupStats([s]) : {};
          const leaders = Object.entries(stats).map(([n, st]) => ({ n, wins: st.wins || 0, ctp: st.ctp || 0 })).filter((r) => r.wins || r.ctp).sort((a, b) => b.wins - a.wins);
          const players = (s.foursomes || []).reduce((n, f) => n + f.players.length, 0);
          return (
            <div key={s.id} style={{ border: "1px solid var(--line)", borderRadius: 12, overflow: "hidden" }}>
              <div style={{ display: "flex", alignItems: "center", gap: 12, padding: "11px 14px", background: "var(--surface-2)", flexWrap: "wrap" }}>
                <span className="serif" style={{ fontSize: 17, fontWeight: 700 }}>{s.name || gm.name}</span>
                <span className="pill pill-accent" style={{ whiteSpace: "nowrap" }}>{gm.name}</span>
                <span className="faint" style={{ fontSize: 12 }}>{Y.shortDate(new Date(s.dateISO + "T00:00:00"))} · {players} player{players === 1 ? "" : "s"}</span>
                <span style={{ marginLeft: "auto" }} className={"pill " + (s.status === "done" ? "" : "pill-green")}>{s.status === "done" ? "Final" : "Active"}</span>
              </div>
              {leaders.length > 0 && (
                <div style={{ display: "flex", gap: 8, flexWrap: "wrap", padding: "11px 14px" }}>
                  {leaders.slice(0, 6).map((r) => (
                    <span key={r.n} style={{ display: "inline-flex", alignItems: "center", gap: 7, padding: "5px 11px 5px 5px", borderRadius: 999, background: "var(--surface-2)", border: "1px solid var(--line)" }}>
                      <Avatar initials={ini(r.n)} size={22} tone={r.n === me ? "accent" : "brand"} />
                      <span style={{ fontSize: 12.5, fontWeight: 700 }}>{r.n === me ? "You" : r.n.split(" ")[0]}</span>
                      {r.wins > 0 && <span className="pill pill-accent" style={{ padding: "1px 7px", fontSize: 10.5 }}><Icon name="trophy" size={10} /> {r.wins}</span>}
                      {r.ctp > 0 && <span className="pill" style={{ padding: "1px 7px", fontSize: 10.5, background: "color-mix(in srgb,var(--accent) 16%,transparent)", color: "var(--accent-deep)" }}><Icon name="pin" size={10} /> {r.ctp}</span>}
                    </span>
                  ))}
                </div>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

Object.assign(window, { OpenGroupScreen, OpenGroupSetup, OpenSessionView, OpenSessionHistory, ogCandidates });
