/* ============================================================
   Screens: Login, Dashboard, Booking, MyBookings, Profile
   ============================================================ */

/* -------------------- LOGIN -------------------- */
function LoginScreen({ supabaseReady }) {
  const db = window.YCCdb;
  const [mode, setMode] = useState("signin"); // signin | signup
  const [email, setEmail] = useState("");
  const [pw, setPw] = useState("");
  const [err, setErr] = useState("");
  const [msg, setMsg] = useState("");
  const [busy, setBusy] = useState(false);

  function google() {
    setErr(""); setMsg("");
    if (!supabaseReady) { setErr("Sign-in isn't configured yet — add your Supabase keys."); return; }
    db.auth.signInWithGoogle().catch(() => setErr("Couldn't start Google sign-in."));
  }

  function submit(e) {
    e.preventDefault();
    setErr(""); setMsg("");
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { setErr("Enter a valid email address."); return; }
    if (pw.length < 6) { setErr("Password must be at least 6 characters."); return; }
    if (!supabaseReady) { setErr("Sign-in isn't configured yet — add your Supabase keys."); return; }
    setBusy(true);
    const p = mode === "signup" ? db.auth.signUpEmail(email, pw) : db.auth.signInEmailPassword(email, pw);
    let settled = false;
    const timer = setTimeout(() => {
      if (!settled) { setBusy(false); setErr("Sign-in is taking too long — refresh the page and try again."); }
    }, 9000);
    p.then((res) => {
      settled = true; clearTimeout(timer);
      const error = res && res.error;
      if (error) { setBusy(false); setErr(error.message || "Sign-in failed."); return; }
      if (mode === "signup") {
        const user = res.data && res.data.user;
        const session = res.data && res.data.session;
        // Supabase returns a user with an EMPTY identities array when the email already exists.
        if (user && Array.isArray(user.identities) && user.identities.length === 0) {
          setBusy(false);
          setErr("That email already has an account. Use “Sign in” instead — or have the manager delete it in Supabase → Authentication → Users to start over.");
          return;
        }
        if (session) { setBusy(false); return; } // confirmation OFF + auto-session → listener swaps in
        // No session: either confirmation is ON, or we can sign in now.
        const confirmed = user && (user.email_confirmed_at || user.confirmed_at);
        if (!confirmed) {
          setBusy(false);
          setErr("Your Supabase project still requires email confirmation. Turn OFF “Confirm email” (Authentication → Sign In / Providers → Email → Save), delete this user under Authentication → Users, then create the account again.");
          return;
        }
        db.auth.signInEmailPassword(email, pw).then((r2) => {
          setBusy(false);
          const sess = r2 && (r2.session || (r2.data && r2.data.session));
          if (sess) return;
          setMsg("Account created — sign in with your email and password.");
        }).catch(() => { setBusy(false); setMsg("Account created — now sign in."); });
        return;
      }
      setBusy(false);
      // Sign-in success with a session → the auth listener swaps to the app automatically.
    }).catch((e) => { settled = true; clearTimeout(timer); setBusy(false); setErr((e && e.message) || "Something went wrong. Try again."); });
  }

  return (
    <div style={{ minHeight: "100vh", display: "grid", gridTemplateColumns: "1fr 1fr" }} className="login-wrap">
      {/* left: brand panel */}
      <div className="login-hero" style={{
        background: "var(--brand-deep)", color: "var(--on-brand)", position: "relative",
        padding: "56px 56px", display: "flex", flexDirection: "column", justifyContent: "space-between",
        backgroundImage: "radial-gradient(120% 80% at 0% 0%, rgba(180,144,78,.18), transparent 55%)",
      }}>
        <div className="brandmark" style={{ gap: 14 }}>
          <div className="crest" style={{ width: 46, height: 46, fontSize: 18 }}>YCC</div>
          <div className="brand-text">
            <div className="b1" style={{ fontSize: 22, whiteSpace: "nowrap" }}>Yardley Country Club</div>
            <div className="b2">DIV Tour · Yardley, PA</div>
          </div>
        </div>
        <div style={{ maxWidth: 430, display: "flex", flexDirection: "column", gap: 30 }}>
          <div>
            <div className="eyebrow" style={{ color: "var(--accent)" }}>DIV Tour · Member Portal</div>
            <h1 className="serif" style={{ fontSize: 40, fontWeight: 700, lineHeight: 1.2, marginTop: 14, letterSpacing: "-.015em" }}>
              Your weekend on the fairway, booked in seconds.
            </h1>
          </div>
          <p style={{ color: "rgba(244,241,230,.72)", fontSize: 15.5, margin: 0, lineHeight: 1.55 }}>
            Reserve weekly tee times, manage your group, and run the Saturday skins game — all from one place.
          </p>
        </div>
        <div style={{ display: "flex", gap: 26, color: "rgba(244,241,230,.6)", fontSize: 12.5 }}>
          <span style={{ display: "flex", alignItems: "center", gap: 7 }}><Icon name="flag" size={15} /> Championship 18</span>
          <span style={{ display: "flex", alignItems: "center", gap: 7 }}><Icon name="shield" size={15} /> USGA / GHIN</span>
        </div>
      </div>

      {/* right: form */}
      <div style={{ display: "grid", placeItems: "center", padding: "40px 28px", background: "var(--bg)" }}>
        <div style={{ width: "100%", maxWidth: 384 }} className="fade-in">
          <h2 className="serif" style={{ fontSize: 32, fontWeight: 600 }}>{mode === "signup" ? "Create your account" : "Welcome"}</h2>
          <p className="muted" style={{ fontSize: 14.5, marginTop: 6 }}>{mode === "signup" ? "Sign up with your email to join the DIV Tour." : "Sign in with your email to access the DIV Tour."}</p>

          <form onSubmit={submit} style={{ marginTop: 24 }}>
            <div className="field">
              <label>Email address</label>
              <input className={"input" + (err ? " err" : "")} type="email" autoFocus placeholder="you@email.com" value={email} onChange={(e) => { setEmail(e.target.value); setErr(""); }} />
            </div>
            <div className="field">
              <label>Password</label>
              <input className="input" type="password" placeholder={mode === "signup" ? "Create a password" : "••••••••"} value={pw} onChange={(e) => setPw(e.target.value)} />
              {err && <div className="err-msg">{err}</div>}
              {msg && <div style={{ fontSize: 12, color: "var(--ok)", marginTop: 6, fontWeight: 600 }}>{msg}</div>}
            </div>
            <button className="btn btn-primary btn-block" type="submit" disabled={busy} style={{ marginTop: 6, padding: "13px" }}>
              {busy ? "Please wait…" : mode === "signup" ? <>Create account <Icon name="arrowR" size={16} /></> : <>Sign in <Icon name="arrowR" size={16} /></>}
            </button>
          </form>

          <p className="faint" style={{ fontSize: 13, marginTop: 22, textAlign: "center", lineHeight: 1.6 }}>
            {mode === "signup" ? "Already have an account? " : "New to the DIV Tour? "}
            <a style={{ color: "var(--accent-deep)", fontWeight: 700, textDecoration: "none", cursor: "pointer" }} onClick={() => { setMode(mode === "signup" ? "signin" : "signup"); setErr(""); setMsg(""); }}>
              {mode === "signup" ? "Sign in" : "Create one"}
            </a>
          </p>
          {!supabaseReady && (
            <p style={{ fontSize: 11.5, marginTop: 18, textAlign: "center", color: "var(--ink-faint)", lineHeight: 1.5 }}>
              Sign-in requires Supabase keys in <code>config.js</code>.
            </p>
          )}
        </div>
      </div>
    </div>
  );
}

/* -------------------- DASHBOARD -------------------- */
function Dashboard({ ctx }) {
  const { member, bookings, slotOf, go, daysUntil, tour, gameConfig } = ctx;
  const Y = window.YCC;
  const Gm = window.YCC_GAMES, Eng = window.YCC_SCORING;
  const satG = Gm.gameById(Eng.whichGame(gameConfig, tour.satISO, tour.satISO));
  const sunG = Gm.gameById(Eng.whichGame(gameConfig, tour.sunISO, tour.satISO));
  const sameGame = satG.id === sunG.id;
  const sorted = [...bookings].sort((a, b) => a.dateISO.localeCompare(b.dateISO));
  const nextB = sorted[0] || null;
  const nextSlot = nextB ? slotOf(nextB) : null;
  const nextDateLabel = nextB ? Y.fmtDate(new Date(nextB.dateISO + "T00:00:00")) : "";
  const hour = new Date().getHours();
  const greet = hour < 12 ? "Good morning" : hour < 17 ? "Good afternoon" : "Good evening";

  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">{window.YCC.fmtDate(window.YCC.TODAY)}</div>
          <h1 style={{ marginTop: 6 }}>{greet}, {member.first}.</h1>
          <p>The DIV Tour entry for the weekend of {tour.weekendLabel} is published. Claim your spot and your handicap travels with you.</p>
        </div>
        <button className="btn btn-accent" onClick={() => go("book")} style={{ padding: "13px 22px" }}>
          <Icon name="plus" size={17} /> {nextB ? "View tee times" : "Claim a spot"}
        </button>
      </div>

      <div className="grid" style={{ gridTemplateColumns: "1.55fr 1fr", alignItems: "start" }}>
        {/* next tee time */}
        <div className="stack">
          {nextSlot ? (
            <div className="card" style={{ overflow: "hidden" }}>
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "16px var(--card-pad)", background: "var(--brand)", color: "var(--on-brand)" }}>
                <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
                  <Icon name="flag" size={18} style={{ color: "var(--accent)" }} />
                  <span style={{ fontWeight: 700, letterSpacing: ".04em", fontSize: 12.5, textTransform: "uppercase" }}>Your next tee time</span>
                </div>
                <span className="pill" style={{ background: "var(--accent)", color: "#1b1404" }}>{daysUntil(nextB.dateISO)}</span>
              </div>
              <div className="card-pad" style={{ display: "flex", gap: 28, flexWrap: "wrap" }}>
                <div style={{ flex: "1 1 200px" }}>
                  <div className="serif" style={{ fontSize: 40, fontWeight: 700, lineHeight: 1 }}>{nextSlot.time}</div>
                  <div style={{ marginTop: 6, fontSize: 15, fontWeight: 600 }}>{nextDateLabel}</div>
                  <div className="muted" style={{ fontSize: 13.5, marginTop: 2, display: "flex", alignItems: "center", gap: 6 }}>
                    <Icon name="pin" size={14} /> Championship 18 · DIV Tour
                  </div>
                  <div style={{ display: "flex", gap: 9, marginTop: 18 }}>
                    <button className="btn btn-quiet btn-sm" onClick={() => go("bookings")}><Icon name="edit" size={14} /> Manage</button>
                    <button className="btn btn-ghost btn-sm" onClick={() => go("games")}><Icon name="trophy" size={14} /> Skins game</button>
                  </div>
                </div>
                <div style={{ flex: "1 1 200px", borderLeft: "1px solid var(--line)", paddingLeft: 26 }}>
                  <div className="eyebrow" style={{ marginBottom: 12 }}>Your group · {nextSlot.players.length} of 4</div>
                  <div className="stack" style={{ gap: 10 }}>
                    {nextSlot.players.map((p, i) => (
                      <div key={i} style={{ display: "flex", alignItems: "center", gap: 10 }}>
                        <Avatar initials={p.initials} size={30} tone={p.name === member.name ? "accent" : "brand"} />
                        <div style={{ minWidth: 0 }}>
                          <div style={{ fontSize: 13.5, fontWeight: p.name === member.name ? 700 : 600, whiteSpace: "nowrap" }}>{p.name === member.name ? p.name + " (you)" : p.name}</div>
                          <div className="faint" style={{ fontSize: 11, fontFamily: "ui-monospace, monospace", whiteSpace: "nowrap", display: "flex", alignItems: "center", gap: 6 }}>HI {p.hi} · GHIN {p.ghin} <Icon name={p.transport === "ride" ? "cart" : "walk"} size={12} style={{ color: "var(--ink-faint)" }} /></div>
                        </div>
                      </div>
                    ))}
                  </div>
                </div>
              </div>
            </div>
          ) : (
            <div className="card card-pad" style={{ textAlign: "center", padding: 40 }}>
              <Icon name="calendar" size={30} style={{ color: "var(--ink-faint)" }} />
              <h3 className="serif" style={{ fontSize: 22, marginTop: 12 }}>You haven't claimed a spot yet</h3>
              <p className="muted" style={{ fontSize: 14, margin: "6px 0 18px" }}>The DIV Tour entry is open — claim an open spot for Saturday or Sunday.</p>
              <button className="btn btn-primary" onClick={() => go("book")}><Icon name="plus" size={16} /> Claim a spot</button>
            </div>
          )}

          {/* conditions */}
          <ConditionsCard />
        </div>

        {/* right column */}
        <div className="stack">
          <div className="card card-pad">
            <div className="eyebrow">Handicap · GHIN</div>
            <div style={{ display: "flex", alignItems: "baseline", gap: 10, marginTop: 8 }}>
              <span className="serif" style={{ fontSize: 46, fontWeight: 600, color: "var(--brand)", lineHeight: 1 }}>{member.handicapIndex ?? "—"}</span>
              <span className="muted" style={{ fontSize: 13 }}>Index</span>
            </div>
            <div className="hairline" style={{ margin: "16px 0" }}></div>
            <div style={{ display: "flex", justifyContent: "space-between", fontSize: 13.5 }}>
              <span className="muted" style={{ whiteSpace: "nowrap" }}>GHIN number</span>
              <span style={{ fontWeight: 700, fontFamily: "ui-monospace, monospace", letterSpacing: ".04em" }}>{member.ghin || "—"}</span>
            </div>
            <div style={{ display: "flex", justifyContent: "space-between", fontSize: 13.5, marginTop: 9 }}>
              <span className="muted" style={{ whiteSpace: "nowrap" }}>Course handicap</span>
              <span style={{ fontWeight: 700 }}>{member.handicapIndex == null ? "—" : window.YCC.courseHandicapForTee(member.handicapIndex, member.tee || "Blue")} <span className="faint" style={{ fontWeight: 500 }}>· {member.tee || "Blue"}</span></span>
            </div>
            {member.handicapIndex == null && (
              <p className="faint" style={{ fontSize: 11.5, marginTop: 10, lineHeight: 1.5 }}>Add your GHIN and handicap in your profile.</p>
            )}
            <button className="btn btn-ghost btn-sm btn-block" style={{ marginTop: 16 }} onClick={() => go("profile")}>View profile</button>
          </div>

          <div className="card card-pad">
            <div className="eyebrow" style={{ marginBottom: 8 }}>This weekend's games</div>
            {sameGame ? (
              <>
                <h3 className="serif" style={{ fontSize: 21, marginBottom: 5 }}>{satG.name}</h3>
                <p className="muted" style={{ fontSize: 13, marginBottom: 16, lineHeight: 1.5 }}>{satG.blurb}</p>
              </>
            ) : (
              <div className="stack" style={{ gap: 8, marginBottom: 16 }}>
                {[["Saturday", satG], ["Sunday", sunG]].map(([d, gm]) => (
                  <div key={d} style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 10, padding: "10px 13px", background: "var(--surface-2)", borderRadius: "var(--radius-sm)" }}>
                    <span style={{ fontSize: 11.5, fontWeight: 700, letterSpacing: ".03em", textTransform: "uppercase", color: "var(--ink-soft)" }}>{d}</span>
                    <span className="serif" style={{ fontSize: 17, fontWeight: 700 }}>{gm.name}</span>
                  </div>
                ))}
              </div>
            )}
            <button className="btn btn-quiet btn-sm btn-block" onClick={() => go("games")}>
              <Icon name="trophy" size={14} /> Open scorecards
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

/* -------------------- BOOKING (claim a spot in the DIV Tour entry) -------------------- */
function SeatFilled({ p, you, onRemove, onMove, onTransport, approved }) {
  return (
    <div style={{
      display: "flex", alignItems: "center", gap: 10, padding: "10px 12px", borderRadius: "var(--radius-sm)",
      background: you ? "color-mix(in srgb,var(--accent) 14%,var(--surface))" : "var(--surface-2)",
      border: "1px solid " + (you ? "var(--accent)" : "transparent"),
    }}>
      <Avatar initials={p.initials} size={32} tone={you ? "accent" : "brand"} />
      <div style={{ minWidth: 0, flex: 1 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 6, flexWrap: "wrap", rowGap: 4 }}>
          <span style={{ fontSize: 13.5, fontWeight: 700, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{you ? p.name + " (you)" : p.name}</span>
          {p.guest && <span className="pill" style={{ background: "var(--surface)", color: "var(--ink-faint)", fontSize: 9, padding: "2px 6px", border: "1px solid var(--line)" }}>Off-app</span>}
          {approved
            ? <span className="pill" style={{ background: "var(--brand-soft)", color: "var(--brand)", fontSize: 9, padding: "2px 6px" }}><Icon name="check" size={9} /> Approved</span>
            : <span className="pill" style={{ background: "color-mix(in srgb,var(--accent) 16%,transparent)", color: "var(--accent-deep)", fontSize: 9, padding: "2px 6px" }} title="Manager must confirm this member's handicap for the week">Pending HCP</span>}
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 3, flexWrap: "wrap", rowGap: 5 }}>
          <span className="faint" style={{ fontSize: 11.5, whiteSpace: "nowrap", fontFamily: "ui-monospace, monospace" }}>HI {p.hi} · GHIN {p.ghin}</span>
          {onTransport
            ? <Transport mode={p.transport || "walk"} onToggle={onTransport} />
            : <Transport mode={p.transport || "walk"} />}
        </div>
      </div>
      <div style={{ display: "flex", gap: 6, flex: "0 0 auto" }}>
        {onMove && (
          <button onClick={onMove} title="Move to another group" style={{ width: 24, height: 24, borderRadius: "50%", display: "grid", placeItems: "center", color: "var(--brand)", border: "1px solid var(--line-strong)" }}>
            <Icon name="swap" size={12} />
          </button>
        )}
        {onRemove && (
          <button onClick={onRemove} title="Remove from group" style={{ width: 24, height: 24, borderRadius: "50%", display: "grid", placeItems: "center", color: "var(--danger)", border: "1px solid color-mix(in srgb,var(--danger) 30%,transparent)" }}>
            <Icon name="x" size={12} />
          </button>
        )}
      </div>
    </div>
  );
}
function SeatOpen({ onClaim, canClaim, manager }) {
  if (manager) {
    return (
      <button onClick={onClaim} style={{
        display: "flex", alignItems: "center", gap: 10, padding: "10px 12px", borderRadius: "var(--radius-sm)",
        border: "1px dashed var(--line-strong)", background: "transparent", textAlign: "left",
        cursor: "pointer", transition: "all .14s", width: "100%",
      }}
      onMouseEnter={(e) => { e.currentTarget.style.borderColor = "var(--accent-deep)"; e.currentTarget.style.background = "color-mix(in srgb,var(--accent) 8%,transparent)"; }}
      onMouseLeave={(e) => { e.currentTarget.style.borderColor = "var(--line-strong)"; e.currentTarget.style.background = "transparent"; }}>
        <span style={{ width: 32, height: 32, borderRadius: "50%", border: "1px dashed var(--accent-deep)", display: "grid", placeItems: "center", color: "var(--accent-deep)", flex: "0 0 auto" }}>
          <Icon name="plus" size={14} />
        </span>
        <span style={{ fontSize: 13, fontWeight: 600, color: "var(--accent-deep)" }}>Add a member</span>
      </button>
    );
  }
  return (
    <button onClick={canClaim ? onClaim : undefined} disabled={!canClaim} style={{
      display: "flex", alignItems: "center", gap: 10, padding: "10px 12px", borderRadius: "var(--radius-sm)",
      border: "1px dashed var(--line-strong)", background: "transparent", textAlign: "left",
      cursor: canClaim ? "pointer" : "default", transition: "all .14s", width: "100%",
    }}
    onMouseEnter={(e) => { if (canClaim) { e.currentTarget.style.borderColor = "var(--brand)"; e.currentTarget.style.background = "var(--brand-soft)"; } }}
    onMouseLeave={(e) => { e.currentTarget.style.borderColor = "var(--line-strong)"; e.currentTarget.style.background = "transparent"; }}>
      <span style={{ width: 32, height: 32, borderRadius: "50%", border: "1px dashed var(--line-strong)", display: "grid", placeItems: "center", color: "var(--ink-faint)", flex: "0 0 auto" }}>
        <Icon name={canClaim ? "plus" : "user"} size={14} />
      </span>
      <span style={{ fontSize: 13, fontWeight: 600, color: canClaim ? "var(--brand)" : "var(--ink-faint)" }}>
        {canClaim ? "Claim this spot" : "Open"}
      </span>
    </button>
  );
}

function BookingScreen({ ctx }) {
  const Y = window.YCC;
  const { tour, dayKeys, member, requestClaim, role, t, go, activeManager, removeSignup, requestManagerMove, requestAddPlayer, setTransport, managerSwapPlayers, managerMovePlayer, phase, entry, confirmedNames, requestManagerEmail, primaryManager } = ctx;
  const [activeDay, setActiveDay] = useState(dayKeys[0].iso);
  const dragRef = useRef(null);
  const [dragName, setDragName] = useState(null);
  const slots = Y.sortByTime(tour.slots[activeDay] || []);
  const dayNote = (tour.notes || {})[activeDay];
  const flow = t.bookingFlow || "sheet";
  const isManager = role === "manager";
  const myDaySlot = slots.find(s => s.players.some(p => p.name === member.name));
  const memberOpen = phase === "open";          // members can self-serve only while open
  const managerLocked = false;                    // managers keep control through game day
  const isApproved = (p) => p.guest || (confirmedNames && confirmedNames.has(p.name));
  const fmtET = (d) => d.toLocaleString("en-US", { weekday: "short", month: "short", day: "numeric", hour: "numeric", minute: "2-digit" });
  // game-day drag/drop: drop A onto B → swap; drop A onto an open seat → move.
  function handleSeatDrop(dstSlotId, dstName) {
    const src = dragRef.current; dragRef.current = null; setDragName(null);
    if (!src) return;
    if (dstName) managerSwapPlayers(activeDay, src.slotId, src.name, dstSlotId, dstName);
    else if (src.slotId !== dstSlotId) managerMovePlayer(activeDay, src.slotId, dstSlotId, src.name);
  }

  if (!tour.published && !isManager) {
    return (
      <div className="page fade-in">
        <div className="card card-pad" style={{ textAlign: "center", padding: 56, maxWidth: 520, margin: "40px auto" }}>
          <Icon name="clock" size={30} style={{ color: "var(--ink-faint)" }} />
          <h3 className="serif" style={{ fontSize: 24, marginTop: 12 }}>This weekend isn't published yet</h3>
          <p className="muted" style={{ fontSize: 14.5, margin: "8px 0 0" }}>{activeManager.name} hasn't released the DIV Tour tee times for {tour.weekendLabel}. You'll be notified by email when sign-up opens.</p>
        </div>
      </div>
    );
  }

  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 · Weekend of {tour.weekendLabel}</div>
          <h1 style={{ marginTop: 6 }}>{isManager ? "Tee sheet" : "Tee times"}</h1>
          <p>{isManager
            ? "Live view of who has claimed a spot in this weekend's published entry. Edit the times in Tour Setup."
            : "Claim an open spot in any group. Your handicap and GHIN are attached automatically when you join."}</p>
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 14px", borderRadius: "var(--radius)", background: "var(--brand-soft)", color: "var(--brand)" }}>
          <Icon name="shield" size={16} />
          <div style={{ fontSize: 12.5, lineHeight: 1.3 }}>
            <div style={{ fontWeight: 700, whiteSpace: "nowrap" }}>Published by {activeManager.name}</div>
            <div style={{ opacity: .75, whiteSpace: "nowrap" }}>{tour.publishedAt}</div>
          </div>
        </div>
      </div>

      {/* sign-up window banner */}
      {(() => {
        const map = {
          open: { bg: "var(--brand-soft)", fg: "var(--brand)", icon: "calendar", text: <>Open sign-ups — claim, move, or release your own spot until <strong>Wednesday 11:59 PM ET</strong> ({fmtET(entry.wed)}). After that, changes go through your manager.</> },
          managerOnly: { bg: "color-mix(in srgb,var(--accent) 14%,var(--surface))", fg: "var(--accent-deep)", icon: "shield", text: <>Member sign-ups are closed. Your <strong>manager</strong> can add, release, or move players until the final lock <strong>Friday 9:00 AM ET</strong> ({fmtET(entry.fri)}). Ask your manager for any changes.</> },
          locked: { bg: "var(--surface-2)", fg: "var(--ink-soft)", icon: "clock", text: <>Groups are <strong>final</strong> — the Friday 9:00 AM ET lock has passed. The tee sheet is set for the weekend.</> },
        }[phase];
        return (
          <div style={{ display: "flex", alignItems: "center", gap: 11, padding: "12px 16px", borderRadius: "var(--radius)", background: map.bg, color: map.fg, border: "1px solid " + (phase === "managerOnly" ? "var(--accent)" : "transparent"), marginBottom: 20, fontSize: 13, lineHeight: 1.45, flexWrap: "wrap" }}>
            <Icon name={map.icon} size={17} style={{ flex: "0 0 auto" }} />
            <div style={{ flex: 1, minWidth: 200 }}>{map.text}</div>
            {!isManager && phase !== "open" && (
              <button className="btn btn-primary btn-sm" onClick={requestManagerEmail} style={{ flex: "0 0 auto" }}><Icon name="mail" size={14} /> Request a change</button>
            )}
          </div>
        );
      })()}

      {/* day tabs */}
      <div style={{ display: "flex", gap: 10, marginBottom: 22, flexWrap: "wrap", alignItems: "center" }}>
        {dayKeys.map((d) => {
          const daySlots = tour.slots[d.iso] || [];
          const open = daySlots.reduce((n, s) => n + (4 - s.players.length), 0);
          const active = activeDay === d.iso;
          return (
            <button key={d.iso} onClick={() => setActiveDay(d.iso)} style={{
              padding: "12px 20px", borderRadius: 12, textAlign: "left",
              border: "1px solid " + (active ? "var(--brand)" : "var(--line)"),
              background: active ? "var(--brand)" : "var(--surface)",
              color: active ? "var(--on-brand)" : "var(--ink)", transition: "all .15s",
            }}>
              <div style={{ fontWeight: 700, fontSize: 15, whiteSpace: "nowrap" }}>{d.label}</div>
              <div style={{ fontSize: 12, opacity: .8, marginTop: 2, whiteSpace: "nowrap" }}>{daySlots.length} tee times · {open} open spots</div>
            </button>
          );
        })}
        {myDaySlot && !isManager && (
          <span className="pill pill-accent" style={{ marginLeft: "auto", whiteSpace: "nowrap" }}><Icon name="check" size={12} /> You're in the {myDaySlot.time} group</span>
        )}
      </div>

      {dayNote && slots.length > 0 && (
        <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "11px 16px", borderRadius: "var(--radius)", background: "color-mix(in srgb,var(--accent) 12%,var(--surface))", border: "1px solid var(--accent)", marginBottom: 14, fontSize: 13, lineHeight: 1.45 }}>
          <Icon name="info" size={16} style={{ color: "var(--accent-deep)", flex: "0 0 auto" }} />
          <div><strong>Club event this day.</strong> {dayNote}</div>
        </div>
      )}

      {isManager && slots.length > 0 && (
        <div style={{ display: "flex", alignItems: "center", gap: 7, marginBottom: 12, fontSize: 12.5, color: "var(--ink-soft)" }}>
          <Icon name="swap" size={14} style={{ color: "var(--accent-deep)" }} /> Drag a player onto another to <strong>swap</strong> their groups, or onto an open seat to move them.
        </div>
      )}

      {slots.length === 0 ? (
        <div className="card card-pad" style={{ textAlign: "center", padding: 48 }}>
          <Icon name={dayNote ? "info" : "calendar"} size={28} style={{ color: dayNote ? "var(--accent-deep)" : "var(--ink-faint)" }} />
          <h3 className="serif" style={{ fontSize: 21, marginTop: 12 }}>{dayNote ? "No DIV Tour group times this day" : "No tee times set for this day"}</h3>
          <p className="muted" style={{ fontSize: 14, marginTop: 6, maxWidth: 520, marginLeft: "auto", marginRight: "auto", lineHeight: 1.5 }}>{dayNote || (isManager ? "Add times in Tour Setup." : "Check back once the manager publishes them.")}</p>
        </div>
      ) : flow === "grid" ? (
        <div className="grid" style={{ gridTemplateColumns: "repeat(auto-fill, minmax(220px,1fr))" }}>
          {slots.map((slot) => {
            const mine = slot.players.some(p => p.name === member.name);
            const open = 4 - slot.players.length;
            const canClaim = !isManager && open > 0 && !mine && !myDaySlot && memberOpen;
            return (
              <div key={slot.id} className="card card-pad" style={{ padding: 18, border: mine ? "1px solid var(--accent)" : "1px solid var(--line)" }}>
                <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 12 }}>
                  <div className="serif" style={{ fontSize: 24, fontWeight: 700 }}>{slot.time}</div>
                  <span style={{ fontSize: 11.5, fontWeight: 600, color: mine ? "var(--accent-deep)" : open ? "var(--ok)" : "var(--ink-faint)" }}>{mine ? "You're in" : open ? `${open} open` : "Full"}</span>
                </div>
                <div style={{ display: "flex", gap: 5, marginBottom: 14 }}>
                  {[0, 1, 2, 3].map(i => {
                    const p = slot.players[i];
                    return (
                      <span key={i} title={p ? `${p.name} · HI ${p.hi} · GHIN ${p.ghin}` : "Open"} style={{
                        flex: 1, height: 34, borderRadius: 7, display: "grid", placeItems: "center", fontSize: 11, fontWeight: 700,
                        background: p ? (p.name === member.name ? "var(--accent)" : "var(--brand-soft)") : "var(--bg)",
                        color: p ? (p.name === member.name ? "#1b1404" : "var(--brand)") : "var(--ink-faint)",
                        border: p ? "none" : "1px dashed var(--line-strong)",
                      }}>{p ? p.initials : ""}</span>
                    );
                  })}
                </div>
                {isManager ? (
                  managerLocked ? (
                    <div className="faint" style={{ fontSize: 12, textAlign: "center" }}>{slot.players.length} of 4 · final</div>
                  ) : open ? (
                    <button className="btn btn-ghost btn-sm btn-block" onClick={() => requestAddPlayer(activeDay, slot)}><Icon name="plus" size={13} /> Add member</button>
                  ) : (
                    <div className="faint" style={{ fontSize: 12, textAlign: "center" }}>Group full · 4 of 4</div>
                  )
                ) : mine ? (
                  <button className="btn btn-ghost btn-sm btn-block" onClick={() => go("bookings")}>Manage my spot</button>
                ) : !memberOpen ? (
                  <button className="btn btn-ghost btn-sm btn-block" disabled>Sign-ups closed</button>
                ) : myDaySlot ? (
                  <button className="btn btn-ghost btn-sm btn-block" disabled title={`You're already in the ${myDaySlot.time} group`}>One spot per day</button>
                ) : open ? (
                  <button className="btn btn-accent btn-sm btn-block" onClick={() => requestClaim(activeDay, slot)}><Icon name="plus" size={13} /> Claim a spot</button>
                ) : (
                  <button className="btn btn-ghost btn-sm btn-block" disabled>Group full</button>
                )}
              </div>
            );
          })}
        </div>
      ) : (
        <div className="stack">
          {slots.map((slot) => {
            const mine = slot.players.some(p => p.name === member.name);
            const open = 4 - slot.players.length;
            const canClaim = !isManager && open > 0 && !mine && !myDaySlot && memberOpen;
            return (
              <div key={slot.id} className="card" style={{ overflow: "hidden", border: mine ? "1px solid var(--accent)" : "1px solid var(--line)" }}>
                <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 14, padding: "16px var(--card-pad)", borderBottom: "1px solid var(--line)", flexWrap: "wrap" }}>
                  <div style={{ display: "flex", alignItems: "baseline", gap: 14 }}>
                    <span className="serif" style={{ fontSize: 26, fontWeight: 700, whiteSpace: "nowrap" }}>{slot.time}</span>
                    <span style={{ fontSize: 13, fontWeight: 600, color: mine ? "var(--accent-deep)" : open ? "var(--ok)" : "var(--ink-faint)" }}>
                      {mine ? "You're in this group" : open ? `${open} open spot${open > 1 ? "s" : ""}` : "Group full"}
                    </span>
                  </div>
                  {isManager ? (
                    <span className="pill pill-green">{slot.players.length} / 4 claimed</span>
                  ) : mine ? (
                    <button className="btn btn-ghost btn-sm" onClick={() => go("bookings")}><Icon name="edit" size={13} /> Manage my spot</button>
                  ) : !memberOpen ? (
                    <span className="pill" style={{ background: "var(--surface-2)", color: "var(--ink-faint)", whiteSpace: "nowrap" }}>Sign-ups closed</span>
                  ) : myDaySlot ? (
                    <span className="pill" style={{ background: "var(--surface-2)", color: "var(--ink-faint)", whiteSpace: "nowrap" }} title={`You're already in the ${myDaySlot.time} group`}>One spot per day</span>
                  ) : open ? (
                    <button className="btn btn-accent btn-sm" onClick={() => requestClaim(activeDay, slot)}><Icon name="plus" size={14} /> Claim a spot</button>
                  ) : (
                    <span className="pill" style={{ background: "var(--line-strong)", color: "var(--ink-soft)" }}>Full</span>
                  )}
                </div>
                <div className="card-pad" style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(190px, 1fr))", gap: 10 }}>
                  {[0, 1, 2, 3].map(i => {
                    const p = slot.players[i];
                    const dropZone = isManager ? {
                      onDragOver: (e) => { if (dragRef.current) e.preventDefault(); },
                      onDrop: (e) => { e.preventDefault(); handleSeatDrop(slot.id, p ? p.name : null); },
                    } : {};
                    if (p) {
                      const isYou = p.name === member.name;
                      const key = slot.id + "|" + p.name;
                      return (
                        <div key={i} {...dropZone}
                          draggable={isManager}
                          onDragStart={isManager ? () => { dragRef.current = { slotId: slot.id, name: p.name }; setDragName(key); } : undefined}
                          onDragEnd={() => { dragRef.current = null; setDragName(null); }}
                          style={{ cursor: isManager ? "grab" : "default", opacity: dragName === key ? .45 : 1 }}>
                          <SeatFilled p={p} you={isYou} approved={isApproved(p)}
                            onRemove={isManager ? () => removeSignup(activeDay, slot.id, p.name) : undefined}
                            onMove={isManager ? () => requestManagerMove(activeDay, slot, p) : undefined}
                            onTransport={(isManager || isYou) ? () => setTransport(activeDay, slot.id, p.name, (p.transport || "walk") === "walk" ? "ride" : "walk") : undefined} />
                        </div>
                      );
                    }
                    if (isManager) return <div key={i} {...dropZone} style={{ outline: dragRef.current ? "2px dashed var(--accent)" : "none", outlineOffset: -2, borderRadius: "var(--radius-sm)" }}><SeatOpen manager onClaim={() => requestAddPlayer(activeDay, slot)} /></div>;
                    return <SeatOpen key={i} canClaim={canClaim} onClaim={() => requestClaim(activeDay, slot)} />;
                  })}
                </div>
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

/* -------------------- WEATHER / CONDITIONS -------------------- */
function ConditionsCard() {
  const Y = window.YCC;
  const [wx, setWx] = useState(null);
  const [state, setState] = useState("loading"); // loading | ok | error

  useEffect(() => {
    let alive = true;
    Y.fetchWeather().then((w) => {
      if (!alive) return;
      if (w && w.tempNow != null && !isNaN(w.tempNow)) { setWx(w); setState("ok"); }
      else setState("error");
    });
    return () => { alive = false; };
  }, []);

  const tiles = wx
    ? [
        [`${wx.tempNow}°`, "Now"],
        [isNaN(wx.high) ? "—" : `${wx.high}°`, "Today's high"],
        [`${wx.windMph}`, `mph ${wx.windDir}`.trim()],
        [wx.condition, "Conditions"],
      ]
    : [["—", "Now"], ["—", "Today's high"], ["—", "Wind"], ["—", "Conditions"]];

  return (
    <div className="card card-pad">
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 16, gap: 10, flexWrap: "wrap" }}>
        <h3 className="serif" style={{ fontSize: 20 }}>Course conditions</h3>
        <span className="pill pill-open"><Icon name="check" size={12} /> Cart path: open</span>
      </div>
      <div className="grid" style={{ gridTemplateColumns: "repeat(4,1fr)", gap: 12 }}>
        {tiles.map(([v, l], i) => (
          <div key={i} style={{ background: "var(--surface-2)", borderRadius: "var(--radius-sm)", aspectRatio: "1 / 1", display: "flex", flexDirection: "column", justifyContent: "center", alignItems: "center", textAlign: "center", padding: 10 }}>
            <div className="serif" style={{ fontSize: v.length > 6 ? 18 : 26, fontWeight: 700, color: "var(--brand)", lineHeight: 1.05 }}>{v}</div>
            <div className="faint" style={{ fontSize: 11.5, marginTop: 7 }}>{l}</div>
          </div>
        ))}
      </div>
      <div className="faint" style={{ fontSize: 10.5, marginTop: 12, display: "flex", alignItems: "center", gap: 6 }}>
        <Icon name="pin" size={11} />
        {state === "ok" ? `${Y.YARDLEY_GEO.label} · live, ${wx.source}` : state === "loading" ? "Loading live weather…" : `${Y.YARDLEY_GEO.label} · weather unavailable`}
      </div>
    </div>
  );
}

Object.assign(window, { LoginScreen, Dashboard, BookingScreen, SeatFilled, SeatOpen, ConditionsCard });
