// Shared Direction-B primitives for Marketplace + Community pages
// Spatial Cinema design language — shared tokens
const PAPER = "#1a1612";        // warm dark base (legacy name, kept: used site-wide)
const INK = "#f2ede4";          // warm cream text
const INK_DEEP = "#0b0e0c";     // true cinema base (footer / deep surfaces)
const TEAL = "#0ac5a8";         // refined cinema teal accent
const TEAL_BRIGHT = "#2ee0c4";
const RUST = "#d2693f";         // warm secondary accent
const serif = "'Fraunces', 'Instrument Serif', serif";
const sans = "'Space Grotesk', sans-serif";
const mono = "'JetBrains Mono', monospace";

function Ph({ label, ratio = "1/1", tone = "dark", style = {} }) {
  const bg = tone === "dark" ? "#0f1513" : "#efece6";
  const fg = tone === "dark" ? "rgba(255,255,255,0.4)" : "rgba(242,237,228,0.4)";
  const stripe = tone === "dark"
    ? "repeating-linear-gradient(135deg, rgba(255,255,255,0.05) 0 1px, transparent 1px 10px)"
    : "repeating-linear-gradient(135deg, rgba(0,0,0,0.05) 0 1px, transparent 1px 10px)";
  return (
    <div style={{ aspectRatio: ratio, background: bg, backgroundImage: stripe, borderRadius: 4, position: "relative", overflow: "hidden", display: "flex", alignItems: "center", justifyContent: "center", fontFamily: "'JetBrains Mono', monospace", fontSize: 10, letterSpacing: "0.08em", color: fg, textTransform: "uppercase", ...style }}>
      <span style={{ position: "absolute", top: 10, left: 12 }}>{label}</span>
    </div>
  );
}

function Logo({ variant = "black", size = 28 }) {
  const src = variant === "white" ? "/shape-logo-new-white.png?v=3"
    : variant === "black" ? "/shape-logo-new-black.png?v=3"
    : "/shape-logo-new-white.png?v=3";
  // New logo has the play-icon stacked above the SHAPE wordmark (aspect ~1.87:1), not inline like the old one.
  // Scale so the overall mark reads at a comparable visual weight to the previous inline logo.
  const h = Math.round(size * 1.8);
  return <img className="shape-brand-logo" src={src} alt="Shape" style={{ "--shape-logo-h": `${h}px` }} />;
}

function NavDropdown({ label, items, active, activeMatch }) {
  const [open, setOpen] = React.useState(false);
  const closeTimer = React.useRef(null);
  const cancelClose = () => { if (closeTimer.current) { clearTimeout(closeTimer.current); closeTimer.current = null; } };
  const scheduleClose = () => { cancelClose(); closeTimer.current = setTimeout(() => setOpen(false), 500); };
  const isActive = activeMatch.includes(active);
  return (
    <div style={{ position: "relative", display: "inline-flex", alignItems: "center", height: "100%" }} onMouseEnter={() => { cancelClose(); setOpen(true); }} onMouseLeave={scheduleClose}>
      <a onClick={() => setOpen(o => !o)} style={{ fontSize: 12, letterSpacing: "0.04em", textTransform: "lowercase", color: isActive ? "#f5efe1" : "rgba(245,239,225,0.55)", fontFamily: sans, fontWeight: 300, cursor: "pointer", display: "inline-flex", alignItems: "center", gap: 5, lineHeight: 1 }}>
        {label}<span style={{ fontSize: 9, opacity: 0.5, lineHeight: 1 }}>▾</span>
      </a>
      {/* Invisible hover bridge — fills the gap between trigger and panel so the
          cursor never crosses dead space that could trigger mouseLeave. */}
      {open && <div onMouseEnter={cancelClose} style={{ position: "absolute", top: "100%", left: 0, right: 0, height: 20 }} />}
      {open && (
        <div onMouseEnter={cancelClose} onMouseLeave={scheduleClose} style={{ position: "absolute", top: "calc(100% + 4px)", left: "50%", transform: "translateX(-50%)", minWidth: 220 }}>
          <div style={{ background: "rgba(26,22,18,0.98)", backdropFilter: "blur(14px)", border: "1px solid rgba(242,237,228,0.1)", borderRadius: 8, padding: 10, boxShadow: "0 20px 50px rgba(0,0,0,0.5)" }}>
          {items.map(([n, href]) => (
            <a key={n} href={href} style={{ display: "block", padding: "10px 14px", fontSize: 13, color: "rgba(242,237,228,0.85)", fontFamily: sans, borderRadius: 4, whiteSpace: "nowrap" }}
              onMouseEnter={e => { e.currentTarget.style.background = "rgba(10,197,168,0.12)"; e.currentTarget.style.color = INK; }}
              onMouseLeave={e => { e.currentTarget.style.background = "transparent"; e.currentTarget.style.color = "rgba(242,237,228,0.85)"; }}
            >{n}</a>
          ))}
          </div>
        </div>
      )}
    </div>
  );
}

const SHAPE_NAV_GROUPS = [
  { kind: "link", label: "About", href: "About.html" },
  { kind: "drop", label: "Clients", match: ["Clients", "My Profile", "Overview", "Dashboard", "Client Overview", "Client Dashboard"], items: [["Overview", "Client.html"], ["Dashboard", "ClientDashboard.html"]] },
  { kind: "drop", label: "Trainers", match: ["Trainers", "Trainer Profile", "Trainer Overview", "Trainer Dashboard"], items: [["Overview", "Coach.html"], ["Dashboard", "TrainerDashboard.html"]] },
  { kind: "drop", label: "Nutritionists", match: ["Nutritionists", "Nutritionist Profile", "Nutritionist Overview", "Nutritionist Dashboard", "Recipes"], items: [["Overview", "Nutritionist.html"], ["Dashboard", "NutritionistDashboard.html"], ["Recipes", "Recipes.html"]] },
  { kind: "link", label: "Marketplace", href: "Marketplace.html" },
  { kind: "link", label: "Community", href: "Community.html" },
  { kind: "drop", label: "Rewards", match: ["Rewards", "Shape Score", "Store"], items: [["Shape Score", "Score.html"], ["Shape Store", "Store.html"]] },
  { kind: "link", label: "App", href: "GetApp.html" },
  { kind: "link", label: "Pricing", href: "Pricing.html" },
];

// Role-scoped portal nav — shown instead of the marketing nav once a user
// is signed in, so each profile only sees tabs relevant to it.
const PORTAL_NAV = {
  client: [
    { kind: "link", label: "Dashboard", href: "ClientDashboard.html" },
    { kind: "link", label: "Train", href: "ClientTrain.html" },
    { kind: "link", label: "Nutrition", href: "ClientNutri.html" },
    { kind: "link", label: "Progress", href: "ClientProgress.html" },
    { kind: "link", label: "Community", href: "ClientCommunity.html" },
    { kind: "drop", label: "More", match: ["More", "Habits", "Goals", "Library", "Playlists", "My Team", "Shape Score"], items: [["Habits", "ClientHabits.html"], ["Goals", "ClientGoal.html"], ["Library", "ClientLibrary.html"], ["Playlists", "ClientPlaylists.html"], ["My Team", "ClientTeam.html"], ["Shape Score", "ClientScore.html"], ["My Profile", "ClientProfile.html"]] },
    { kind: "link", label: "Radio", href: "Radio.html" },
  ],
  trainer: [
    { kind: "link", label: "Dashboard", href: "TrainerDashboard.html" },
    { kind: "link", label: "Clients", href: "TrainerClients.html" },
    { kind: "link", label: "Programs", href: "TrainerPrograms.html" },
    { kind: "link", label: "Messages", href: "TrainerMessages.html" },
    { kind: "link", label: "Analytics", href: "TrainerAnalytics.html" },
    { kind: "drop", label: "More", match: ["More", "Community", "Playlists", "Shape Score", "Public profile"], items: [["Community", "TrainerCommunity.html"], ["Playlists", "TrainerPlaylists.html"], ["Shape Score", "TrainerScore.html"], ["Public profile", "TrainerPublic.html"], ["My Profile", "TrainerProfile.html"]] },
    { kind: "link", label: "Radio", href: "Radio.html" },
  ],
  nutritionist: [
    { kind: "link", label: "Dashboard", href: "NutritionistDashboard.html" },
    { kind: "link", label: "Clients", href: "NutritionistClients.html" },
    { kind: "link", label: "Plans", href: "NutritionistPlans.html" },
    { kind: "link", label: "Messages", href: "NutritionistMessages.html" },
    { kind: "link", label: "Analytics", href: "NutritionistAnalytics.html" },
    { kind: "drop", label: "More", match: ["More", "Community", "Playlists", "Shape Score", "Public profile"], items: [["Community", "NutritionistCommunity.html"], ["Playlists", "NutritionistPlaylists.html"], ["Shape Score", "NutritionistScore.html"], ["Public profile", "NutritionistPublic.html"], ["My Profile", "NutritionistProfile.html"]] },
    { kind: "link", label: "Radio", href: "Radio.html" },
  ],
};

// The nav groups to show: role-scoped portal nav when signed in, else marketing.
function navGroupsFor(authUser) {
  if (authUser && authUser.role && PORTAL_NAV[authUser.role]) return PORTAL_NAV[authUser.role];
  if (authUser) return PORTAL_NAV.client;
  return SHAPE_NAV_GROUPS;
}

function MobileDrawer({ open, onClose, active, authUser, onLogout }) {
  React.useEffect(() => {
    document.body.style.overflow = open ? "hidden" : "";
    return () => { document.body.style.overflow = ""; };
  }, [open]);
  if (!open) return null;
  const groups = navGroupsFor(authUser);
  const linkBase = { display: "block", padding: "18px 0", fontFamily: sans, fontSize: 22, letterSpacing: "0.02em", borderBottom: "1px solid rgba(242,237,228,0.08)", textDecoration: "none" };
  return (
    <div role="dialog" aria-modal="true"
      style={{ position: "fixed", inset: 0, zIndex: 100, background: "rgba(26,22,18,0.98)", backdropFilter: "blur(14px)", WebkitBackdropFilter: "blur(14px)", display: "flex", flexDirection: "column", padding: "20px 24px 32px", overflowY: "auto" }}>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 24 }}>
        <a href="index.html" style={{ display: "inline-flex", alignItems: "center" }}><Logo variant="white" size={44} /></a>
        <button onClick={onClose} aria-label="Close menu"
          style={{ background: "transparent", color: INK, border: 0, fontSize: 30, lineHeight: 1, padding: 8, cursor: "pointer", fontFamily: sans }}>×</button>
      </div>
      <nav style={{ flex: 1 }}>
        {groups.map(g => g.kind === "drop" ? (
          <div key={g.label}>
            <div style={{ ...linkBase, color: g.match.includes(active) ? TEAL : INK, fontWeight: 500, borderBottom: "1px solid rgba(242,237,228,0.12)", paddingBottom: 10 }}>{g.label}</div>
            <div style={{ paddingLeft: 14, paddingBottom: 14, borderBottom: "1px solid rgba(242,237,228,0.08)" }}>
              {g.items.map(([n, h]) => (
                <a key={n} href={h} style={{ display: "block", padding: "10px 0", fontFamily: sans, fontSize: 15, color: "rgba(242,237,228,0.72)", textDecoration: "none" }}>{n}</a>
              ))}
            </div>
          </div>
        ) : (
          <a key={g.label} href={g.href} style={{ ...linkBase, color: active === g.label ? TEAL : INK, fontWeight: active === g.label ? 500 : 400 }}>{g.label}</a>
        ))}
      </nav>
      <div style={{ display: "flex", gap: 12, marginTop: 24 }}>
        {authUser ? (
          <a href="#" onClick={onLogout} style={{ flex: 1, textAlign: "center", padding: "14px 18px", borderRadius: 6, border: "1px solid rgba(242,237,228,0.2)", color: INK, fontFamily: sans, fontSize: 14, letterSpacing: "0.08em", textTransform: "uppercase", textDecoration: "none" }}>Sign out</a>
        ) : (
          <>
            <a href="Login.html" style={{ flex: 1, textAlign: "center", padding: "14px 18px", borderRadius: 6, border: "1px solid rgba(242,237,228,0.2)", color: INK, fontFamily: sans, fontSize: 14, letterSpacing: "0.08em", textTransform: "uppercase", textDecoration: "none" }}>Log in</a>
            <a href="Landing.html" style={{ flex: 1, textAlign: "center", padding: "14px 18px", borderRadius: 6, background: INK, color: PAPER, fontFamily: sans, fontSize: 14, fontWeight: 500, letterSpacing: "0.06em", textTransform: "uppercase", textDecoration: "none" }}>Get started</a>
          </>
        )}
      </div>
    </div>
  );
}

function Header({ active }) {
  const [drawerOpen, setDrawerOpen] = React.useState(false);
  const [authUser, setAuthUser] = React.useState(null);
  const [roleMenuOpen, setRoleMenuOpen] = React.useState(false);
  React.useEffect(() => {
    let cancelled = false;
    fetch('/api/me', { credentials: 'same-origin' })
      .then(r => r.ok ? r.json() : { user: null })
      .then(d => { if (!cancelled) setAuthUser(d && d.user ? d.user : null); })
      .catch(() => { if (!cancelled) setAuthUser(null); });
    return () => { cancelled = true; };
  }, []);
  async function handleLogout(e) {
    e.preventDefault();
    try {
      await fetch('/api/auth/signout', { method: 'POST', credentials: 'same-origin' });
    } catch {}
    window.location.href = '/';
  }
  async function switchRole(nextRole) {
    setRoleMenuOpen(false);
    if (!authUser || nextRole === authUser.role) return;
    try {
      const res = await fetch('/api/me/role', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'same-origin',
        body: JSON.stringify({ role: nextRole }),
      });
      const data = await res.json().catch(() => ({}));
      if (res.ok && data.dashboard) {
        window.location.href = data.dashboard;
      } else {
        alert(data.error || 'Could not switch role.');
      }
    } catch (err) {
      console.error('[header] switchRole failed', err);
    }
  }
  const roleLabel = (r) => r === 'trainer' ? 'Trainer' : r === 'nutritionist' ? 'Nutritionist' : 'Client';
  const dashboardHref = (r) => r === 'trainer'
    ? '/newdesign/TrainerDashboard.html'
    : r === 'nutritionist'
      ? '/newdesign/NutritionistDashboard.html'
      : '/newdesign/ClientDashboard.html';
  // Wrap plain-link nav items in the same outer container shape NavDropdown
  // uses, so they share vertical-centering and any future container styles.
  const link = (name, href) => (
    <div style={{ position: "relative", display: "inline-flex", alignItems: "center", height: "100%" }}>
      <a href={href} className="shape-nav-link" style={{ fontSize: 12, letterSpacing: "0.04em", textTransform: "lowercase", color: active === name ? "#f5efe1" : "rgba(245,239,225,0.55)", fontFamily: sans, fontWeight: 300, whiteSpace: "nowrap", lineHeight: 1, display: "inline-flex", alignItems: "center" }}>{name}</a>
    </div>
  );
  return (
    <>
    <header className="shape-header" style={{ position: "fixed", top: 0, left: 0, right: 0, zIndex: 60, background: "rgba(11,14,12,0.55)", backdropFilter: "blur(20px) saturate(1.05)", WebkitBackdropFilter: "blur(20px) saturate(1.05)", borderBottom: "1px solid rgba(245,239,225,0.06)" }}>
      <div aria-hidden style={{ position: "absolute", top: 0, left: 0, right: 0, height: 1, background: `linear-gradient(90deg, transparent 0%, ${TEAL} 30%, ${RUST} 70%, transparent 100%)`, opacity: 0.5 }} />
      <ShapeMobileStyles />
      <div className="shape-header-inner" style={{ maxWidth: 1480, margin: "0 auto", display: "grid", gridTemplateColumns: "auto 1fr auto", alignItems: "center", padding: "10px 72px 10px 72px", gap: 32 }}>
        <a href="index.html" style={{ flex: "none", display: "inline-flex", alignItems: "center", lineHeight: 0 }}>
          <img src="/shape-logo-new-white.png?v=3" alt="Shape" style={{ height: 76, width: "auto", display: "block", objectFit: "contain" }} />
        </a>
        <nav className="shape-nav-tabs" style={{ display: "flex", gap: 18, alignItems: "center", flexWrap: "nowrap", whiteSpace: "nowrap", justifyContent: "center", minWidth: 0 }}>
          {navGroupsFor(authUser).map(g => g.kind === "drop"
            ? <NavDropdown key={g.label} label={g.label} active={active} activeMatch={g.match} items={g.items} />
            : <React.Fragment key={g.label}>{link(g.label, g.href)}</React.Fragment>
          )}
        </nav>
        <div className="shape-nav-auth" style={{ display: "flex", alignItems: "center", gap: 18, flexShrink: 0 }}>
          {authUser ? (
            <>
              <span style={{ fontSize: 12.5, color: INK, fontFamily: sans, fontWeight: 500, whiteSpace: "nowrap", maxWidth: 130, overflow: "hidden", textOverflow: "ellipsis", letterSpacing: "-0.005em" }}>Hi, {authUser.firstName || authUser.email}</span>
              {authUser.roles && authUser.roles.length > 1 ? (
                <div style={{ position: "relative" }} onMouseEnter={() => setRoleMenuOpen(true)} onMouseLeave={() => setRoleMenuOpen(false)}>
                  <button onClick={() => setRoleMenuOpen(v => !v)} style={{ background: "rgba(10,197,168,0.1)", border: `1px solid ${TEAL}`, color: TEAL, fontSize: 10.5, letterSpacing: "0.14em", textTransform: "uppercase", fontFamily: "'JetBrains Mono', monospace", padding: "6px 12px", borderRadius: 999, cursor: "pointer", display: "inline-flex", alignItems: "center", gap: 6, lineHeight: 1, whiteSpace: "nowrap" }}>
                    {roleLabel(authUser.role)} <span style={{ fontSize: 8, opacity: 0.7 }}>▾</span>
                  </button>
                  {roleMenuOpen && (
                    <div style={{ position: "absolute", top: "100%", right: 0, paddingTop: 8, minWidth: 180, zIndex: 60 }}>
                      <div style={{ background: "rgba(26,22,18,0.98)", backdropFilter: "blur(14px)", border: "1px solid rgba(242,237,228,0.1)", borderRadius: 8, padding: 6, boxShadow: "0 20px 50px rgba(0,0,0,0.5)" }}>
                        <div style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 9, letterSpacing: "0.14em", textTransform: "uppercase", color: "rgba(242,237,228,0.45)", padding: "8px 12px 4px" }}>Switch profile</div>
                        {authUser.roles.map(r => (
                          <button key={r} onClick={() => switchRole(r)} disabled={r === authUser.role} style={{ width: "100%", textAlign: "left", background: r === authUser.role ? "rgba(10,197,168,0.12)" : "transparent", border: 0, padding: "9px 12px", fontFamily: sans, fontSize: 13, color: r === authUser.role ? TEAL : "rgba(242,237,228,0.85)", cursor: r === authUser.role ? "default" : "pointer", borderRadius: 4, display: "flex", alignItems: "center", justifyContent: "space-between", lineHeight: 1 }}
                            onMouseEnter={e => { if (r !== authUser.role) { e.currentTarget.style.background = "rgba(10,197,168,0.08)"; e.currentTarget.style.color = INK; } }}
                            onMouseLeave={e => { if (r !== authUser.role) { e.currentTarget.style.background = "transparent"; e.currentTarget.style.color = "rgba(242,237,228,0.85)"; } }}
                          >
                            <span>{roleLabel(r)}</span>
                            {r === authUser.role && <span style={{ fontSize: 10, color: TEAL }}>● active</span>}
                          </button>
                        ))}
                      </div>
                    </div>
                  )}
                </div>
              ) : null}
              <a href={dashboardHref(authUser.role)} style={{ fontSize: 11, letterSpacing: "0.16em", textTransform: "uppercase", color: "rgba(245,239,225,0.66)", fontFamily: mono, whiteSpace: "nowrap", lineHeight: 1, textDecoration: "none" }}>Dashboard</a>
              <a href="#" onClick={handleLogout} style={{ background: "transparent", color: "#f5efe1", border: "1px solid #f5efe1", padding: "11px 22px", borderRadius: 999, fontSize: 12, fontWeight: 300, letterSpacing: "0.04em", textTransform: "lowercase", fontFamily: sans, cursor: "pointer", whiteSpace: "nowrap", textDecoration: "none", display: "inline-flex", alignItems: "center", lineHeight: 1, transition: "background .2s ease, color .2s ease" }}>Sign out</a>
              <a href="/newdesign/Radio.html" aria-label="Shape Radio" style={{ display: "inline-flex", alignItems: "center", lineHeight: 0 }}>
                <img src="/shape-radio-wordmark-tight.svg?v=1" alt="Shape Radio" style={{ width: 170, height: "auto", maxWidth: "none", display: "block" }} />
              </a>
            </>
          ) : (
            <>
              <a href="/newdesign/Login.html" style={{ fontSize: 12, fontWeight: 300, letterSpacing: "0.04em", textTransform: "lowercase", color: "rgba(245,239,225,0.55)", fontFamily: sans, whiteSpace: "nowrap", lineHeight: 1 }}>Log in</a>
              <a href="/newdesign/Landing.html" style={{ background: "transparent", color: "#f5efe1", border: "1px solid #f5efe1", padding: "11px 22px", borderRadius: 999, fontSize: 12, fontWeight: 300, letterSpacing: "0.04em", textTransform: "lowercase", fontFamily: sans, cursor: "pointer", whiteSpace: "nowrap", textDecoration: "none", display: "inline-flex", alignItems: "center", lineHeight: 1, transition: "background .2s ease, color .2s ease" }}>Get started</a>
              <a href="/newdesign/Radio.html" aria-label="Shape Radio" style={{ display: "inline-flex", alignItems: "center", lineHeight: 0 }}>
                <img src="/shape-radio-wordmark-tight.svg?v=1" alt="Shape Radio" style={{ width: 170, height: "auto", maxWidth: "none", display: "block" }} />
              </a>
            </>
          )}
        </div>
        <button className="shape-nav-burger" aria-label="Open menu" onClick={() => setDrawerOpen(true)}
          style={{ display: "none", background: "transparent", border: 0, color: INK, width: 40, height: 40, padding: 0, cursor: "pointer", alignItems: "center", justifyContent: "center" }}>
          <svg width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M4 7h16M4 12h16M4 17h16" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" /></svg>
        </button>
      </div>
      <MobileDrawer open={drawerOpen} onClose={() => setDrawerOpen(false)} active={active} authUser={authUser} onLogout={handleLogout} />
    </header>
    <div aria-hidden className="shape-header-spacer" style={{ height: 96 }} />
    </>
  );
}

function HeroBg() {
  return (
    <>
      <div aria-hidden style={{ position: "absolute", inset: 0, backgroundImage: "url('/get%20started.png')", backgroundSize: "cover", backgroundPosition: "center 40%", pointerEvents: "none" }} />
      {/* Lighter gradient — let the road image breathe */}
      <div aria-hidden style={{ position: "absolute", inset: 0, background: "linear-gradient(180deg, rgba(26,22,18,0.55) 0%, rgba(26,22,18,0.1) 35%, rgba(26,22,18,0.1) 55%, rgba(26,22,18,0.85) 92%, rgba(26,22,18,1) 100%)", pointerEvents: "none" }} />
    </>
  );
}

function Footer() {
  return (
    <footer className="shape-footer" style={{ position: "relative", padding: "56px 72px 36px", background: INK_DEEP, color: INK }}>
      <div aria-hidden style={{ position: "absolute", top: 0, left: 0, right: 0, height: 1, background: `linear-gradient(90deg, transparent 0%, ${TEAL} 30%, ${RUST} 70%, transparent 100%)`, opacity: 0.55 }} />
      <div style={{ maxWidth: 1320, margin: "0 auto" }}>
        <div className="shape-footer-cta" style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 12, paddingBottom: 36, textAlign: "center" }}>
          <img src="/shape-logo-new-white.png?v=3" alt="Shape" style={{ height: 80, width: "auto", display: "block", margin: "0 auto", objectFit: "contain" }} />
          <div style={{ fontFamily: serif, fontSize: 20, fontStyle: "italic", letterSpacing: "-0.02em", color: INK }}>Join the community</div>
        </div>
        <div className="shape-footer-grid" style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 28, paddingTop: 30, borderTop: "1px solid rgba(242,237,228,0.1)", justifyItems: "center", textAlign: "center" }}>
          {[
            ["Product",      [["Marketplace", "Marketplace.html"], ["Shape Score", "Score.html"], ["Radio", "Radio.html"], ["Dashboard", "ClientDashboard.html"]]],
            ["For trainers", [["Apply", "SignupTrainer.html"], ["Payouts", "TrainerDashboard.html"], ["Programs", "TrainerPrograms.html"]]],
            ["Company",      [["About", "About.html"], ["Press", "Team.html#press"]]],
            ["Support",      [["Help", "/help.html"], ["Contact", "/contact.html"], ["Privacy", "/privacy.html"], ["Terms", "/terms.html"]]],
          ].map(([h, items]) => (
            <div key={h}>
              <div style={{ fontFamily: mono, fontSize: 10, letterSpacing: "0.18em", textTransform: "uppercase", color: TEAL, marginBottom: 14 }}>{h}</div>
              {items.map(([label, href]) => <div key={label} style={{ marginBottom: 8 }}><a href={href} className="shape-foot-link" style={{ fontFamily: sans, fontSize: 12.5, color: "rgba(242,237,228,0.72)", textDecoration: "none", transition: "color .15s ease" }}>{label}</a></div>)}
            </div>
          ))}
        </div>
        <div className="shape-footer-base" style={{ marginTop: 36, paddingTop: 18, borderTop: "1px solid rgba(242,237,228,0.08)", display: "flex", justifyContent: "space-between", fontFamily: mono, fontSize: 10, letterSpacing: "0.16em", color: "rgba(242,237,228,0.42)" }}>
          <span>© 2026 SHAPE</span>
          <span>BROOKLYN · LISBON · MELBOURNE</span>
        </div>
      </div>
    </footer>
  );
}

function ShapeMobileStyles() {
  return (
    <style>{`
      html, body { overflow-x: hidden; }
      /* Spatial Cinema chrome micro-interactions */
      .shape-nav-link, .shape-foot-link { transition: color .16s ease, border-color .16s ease; }
      .shape-foot-link:hover { color: ${TEAL_BRIGHT} !important; }
      .shape-nav-link:hover { color: ${INK} !important; }
      .shape-header { transition: background .25s ease; }
      .shape-brand-logo {
        height: var(--shape-logo-h) !important;
        width: auto !important;
        max-width: none !important;
        max-height: none !important;
        object-fit: contain !important;
        display: block !important;
        flex: 0 0 auto;
      }
      @media (max-width: 900px) {
        /* Header */
        .shape-header-inner { padding: 12px 18px !important; gap: 12px !important; }
        .shape-header-spacer { height: 96px !important; }



        .shape-nav-tabs { display: none !important; }
        .shape-nav-auth { display: none !important; }
        .shape-nav-burger { display: inline-flex !important; }

        /* Footer */
        .shape-footer { padding: 32px 22px 24px !important; }
        .shape-footer-grid { grid-template-columns: 1fr 1fr !important; gap: 28px !important; padding-top: 28px !important; }
        .shape-footer-base { flex-direction: column; gap: 10px; align-items: flex-start !important; }

        /* Typography — scale down the huge serif headlines used across the marketing pages.
           Inline font-size on h1/h2/h3 is overridden by these !important rules. */
        h1 { font-size: clamp(40px, 10.5vw, 72px) !important; line-height: 0.96 !important; letter-spacing: -0.03em !important; }
        h2 { font-size: clamp(30px, 8vw, 52px) !important; line-height: 1 !important; letter-spacing: -0.025em !important; }
        h3 { font-size: clamp(22px, 5.2vw, 34px) !important; line-height: 1.1 !important; }

        /* Section padding — every marketing section uses inline "padding: Ypx 72px" (or similar).
           Force horizontal padding down on mobile so content doesn't hug the edge or overflow. */
        section { padding-left: 22px !important; padding-right: 22px !important; }
        footer { padding-left: 22px !important; padding-right: 22px !important; }

        /* Collapse multi-column grids. Attribute selectors match the inline style
           that React serializes (e.g. "grid-template-columns: repeat(4, 1fr)"). */
        [style*="grid-template-columns: 1fr 1fr"],
        [style*="grid-template-columns:1fr 1fr"],
        [style*="grid-template-columns: 1.4fr 1fr"],
        [style*="grid-template-columns: 1.3fr 1fr"],
        [style*="grid-template-columns: 2fr 1fr"],
        [style*="grid-template-columns: repeat(2"],
        [style*="grid-template-columns: repeat(3"],
        [style*="grid-template-columns: repeat(4"],
        [style*="grid-template-columns: repeat(5"] {
          grid-template-columns: 1fr !important;
          gap: 20px !important;
        }
        /* Footer grid is special-cased above (keeps 2 cols) */
        .shape-footer-grid[style*="grid-template-columns"] { grid-template-columns: 1fr 1fr !important; }

        /* Explicit opt-in hooks for pages that want different behaviors */
        [data-mobile="scale"] h1 { font-size: clamp(40px, 11vw, 68px) !important; }
        [data-mobile="scale"] h2 { font-size: clamp(30px, 8vw, 52px) !important; }
        [data-mobile="stack-2"] { grid-template-columns: 1fr 1fr !important; gap: 16px !important; }
      }
      @media (max-width: 520px) {
        .shape-footer-grid { grid-template-columns: 1fr !important; }
        h1 { font-size: clamp(36px, 11vw, 56px) !important; }
        section { padding-left: 18px !important; padding-right: 18px !important; }
      }
    `}</style>
  );
}

Object.assign(window, { PAPER, INK, TEAL, TEAL_BRIGHT, serif, sans, Ph, Logo, Header, Footer, HeroBg });

// -----------------------------------------------------------------------------
// Calendar overlay — shared across Client / Trainer / Nutritionist pages.
// Week + Month views. Filters by role-relevant event types.
// Usage:
//   const cal = useCalendarOverlay();
//   ...
//   <a onClick={cal.open}>Open calendar →</a>
//   <CalendarOverlay {...cal.props} role="client" events={EVENTS} />
// Event shape: { date: "2026-04-18", time: "09:00" | null, kind, title, sub, with? }
// kind ∈ WORKOUT | MEAL | CHECKIN | SESSION | CONSULT | REVIEW | PLAN | ADMIN
// -----------------------------------------------------------------------------

function useCalendarOverlay() {
  const [open, setOpen] = React.useState(false);
  return {
    open: (e) => { if (e) e.preventDefault(); setOpen(true); },
    close: () => setOpen(false),
    props: { open, onClose: () => setOpen(false) },
  };
}

const KIND_COLORS = {
  WORKOUT:  "#0ac5a8",
  SESSION:  "#0ac5a8",
  MEAL:     "#e8b54a",
  PLAN:     "#e8b54a",
  CONSULT:  "#c084e8",
  CHECKIN:  "#6fb5ff",
  REVIEW:   "#6fb5ff",
  ADMIN:    "rgba(242,237,228,0.5)",
};
const KIND_LABEL = { WORKOUT:"Workout", MEAL:"Meal", CHECKIN:"Check-in", SESSION:"Session", CONSULT:"Consult", REVIEW:"Review", PLAN:"Plan", ADMIN:"Admin" };

function ymd(d) { return d.toISOString().slice(0,10); }
function startOfWeek(d) {
  const x = new Date(d);
  const day = (x.getDay() + 6) % 7; // Mon-start
  x.setDate(x.getDate() - day); x.setHours(0,0,0,0);
  return x;
}
function addDays(d, n) { const x = new Date(d); x.setDate(x.getDate()+n); return x; }
function fmtMonth(d) { return d.toLocaleDateString("en-US", { month: "long", year: "numeric" }); }
function fmtWeekRange(s) {
  const e = addDays(s, 6);
  const sm = s.toLocaleDateString("en-US", { month: "short", day: "numeric" });
  const em = e.toLocaleDateString("en-US", { month: s.getMonth()===e.getMonth() ? undefined : "short", day: "numeric" });
  return `${sm} – ${em}, ${e.getFullYear()}`;
}

function CalendarOverlay({ open, onClose, role = "client", events = [], anchorDate = null }) {
  // Anchor to the real today unless an explicit anchorDate is passed (kept for
  // demos/tests). Normalize to local midnight so the "today" highlight is exact.
  const today = React.useMemo(() => {
    const d = anchorDate ? new Date(anchorDate + "T00:00:00") : new Date();
    d.setHours(0, 0, 0, 0);
    return d;
  }, [anchorDate]);
  const [view, setView] = React.useState("week"); // "week" | "month"
  const [cursor, setCursor] = React.useState(() => { const d = anchorDate ? new Date(anchorDate + "T00:00:00") : new Date(); d.setHours(0,0,0,0); return d; });
  const [selected, setSelected] = React.useState(null); // { event, anchor: {x,y,w,h} }
  const [serverEvents, setServerEvents] = React.useState(null); // live events from /api/calendar
  const [adding, setAdding] = React.useState(false);

  // Load live events for a wide window around the cursor whenever the calendar
  // is open. Falls back silently (keeps prop `events`) if the user is signed
  // out or the API is unavailable. Keyed to the cursor's month so paging refetches.
  const monthKey = `${cursor.getFullYear()}-${cursor.getMonth()}`;
  const reload = React.useCallback(() => {
    if (!open) return;
    const from = ymd(addDays(cursor, -45));
    const to = ymd(addDays(cursor, 45));
    fetch(`/api/calendar?from=${from}&to=${to}`, { credentials: "same-origin" })
      .then(r => (r.ok ? r.json() : null))
      .then(d => { if (d && Array.isArray(d.events)) setServerEvents(d.events); })
      .catch(() => {});
  }, [open, monthKey]);
  React.useEffect(() => { reload(); }, [reload]);

  // Server events win when present; otherwise the static prop events show.
  const allEvents = serverEvents != null ? serverEvents : events;
  const live = serverEvents != null;

  const byDate = React.useMemo(() => {
    const m = {};
    allEvents.forEach(e => { (m[e.date] = m[e.date] || []).push(e); });
    Object.values(m).forEach(list => list.sort((a,b) => (a.time||"00:00").localeCompare(b.time||"00:00")));
    return m;
  }, [allEvents]);

  if (!open) return null;

  const shift = (dir) => {
    setSelected(null);
    if (view === "week") setCursor(addDays(cursor, 7*dir));
    else { const x = new Date(cursor); x.setMonth(x.getMonth()+dir); setCursor(x); }
  };
  const setViewAndClose = (v) => { setSelected(null); setView(v); };
  const goToday = () => { setSelected(null); setCursor(today); };

  const roleLabel = { client: "My calendar", trainer: "Coaching calendar", nutritionist: "Nutrition calendar" }[role] || "Calendar";

  return (
    <div role="dialog" aria-modal="true"
      style={{ position: "fixed", inset: 0, zIndex: 200, background: "rgba(10,10,8,0.92)", backdropFilter: "blur(14px)", WebkitBackdropFilter: "blur(14px)", display: "flex", alignItems: "flex-start", justifyContent: "center", padding: "40px 24px", overflow: "auto" }}
      onClick={onClose}>
      <div onClick={e => e.stopPropagation()}
        style={{ width: "min(1200px, 100%)", background: PAPER, color: INK, border: "1px solid rgba(242,237,228,0.1)", borderRadius: 14, boxShadow: "0 40px 120px rgba(0,0,0,0.6)", fontFamily: sans, overflow: "hidden", margin: "auto" }}>
        {/* Header */}
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "22px 28px", borderBottom: "1px solid rgba(242,237,228,0.08)" }}>
          <div style={{ display: "flex", alignItems: "center", gap: 18 }}>
            <div style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 10.5, letterSpacing: "0.18em", color: TEAL_BRIGHT }}>{roleLabel.toUpperCase()}</div>
            <div style={{ fontFamily: serif, fontSize: 26, letterSpacing: "-0.02em" }}>{view === "week" ? fmtWeekRange(startOfWeek(cursor)) : fmtMonth(cursor)}</div>
          </div>
          <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
            <div style={{ display: "inline-flex", border: "1px solid rgba(242,237,228,0.15)", borderRadius: 999, padding: 3 }}>
              {["week","month"].map(v => (
                <button key={v} onClick={() => setViewAndClose(v)}
                  style={{ background: view===v ? INK : "transparent", color: view===v ? PAPER : INK, border: 0, padding: "6px 16px", borderRadius: 999, fontFamily: sans, fontSize: 12, fontWeight: 500, letterSpacing: "0.04em", cursor: "pointer", textTransform: "capitalize" }}>{v}</button>
              ))}
            </div>
            <button onClick={goToday} style={{ background: "transparent", color: "rgba(242,237,228,0.8)", border: "1px solid rgba(242,237,228,0.15)", padding: "7px 14px", borderRadius: 999, fontFamily: sans, fontSize: 12, cursor: "pointer" }}>Today</button>
            <input
              type="date"
              value={ymd(cursor)}
              onChange={(e) => {
                if (!e.target.value) return;
                setSelected(null);
                setCursor(new Date(e.target.value + "T00:00:00"));
              }}
              aria-label="Jump to date"
              style={{ background: "transparent", color: "rgba(242,237,228,0.8)", border: "1px solid rgba(242,237,228,0.15)", padding: "6px 12px", borderRadius: 999, fontFamily: sans, fontSize: 12, cursor: "pointer", colorScheme: "dark" }}
            />
            <div style={{ display: "inline-flex", gap: 2 }}>
              <button onClick={() => shift(-1)} style={navArrowStyle}>‹</button>
              <button onClick={() => shift(1)} style={navArrowStyle}>›</button>
            </div>
            {live && (
              <button onClick={() => setAdding(true)} style={{ background: TEAL, color: "#031f1c", border: 0, padding: "7px 16px", borderRadius: 999, fontFamily: sans, fontSize: 12, fontWeight: 700, cursor: "pointer", letterSpacing: "0.02em" }}>+ Add</button>
            )}
            <button onClick={onClose} aria-label="Close" style={{ background: "transparent", color: "rgba(242,237,228,0.6)", border: 0, fontSize: 22, padding: "2px 10px", cursor: "pointer", marginLeft: 6 }}>×</button>
          </div>
        </div>

        {/* Legend */}
        <div style={{ display: "flex", gap: 18, padding: "12px 28px", borderBottom: "1px solid rgba(242,237,228,0.06)", flexWrap: "wrap" }}>
          {legendForRole(role).map(k => (
            <div key={k} style={{ display: "inline-flex", alignItems: "center", gap: 8, fontSize: 11.5, fontFamily: sans, color: "rgba(242,237,228,0.65)" }}>
              <span style={{ width: 9, height: 9, borderRadius: 2, background: KIND_COLORS[k] }} />
              {KIND_LABEL[k]}
            </div>
          ))}
        </div>

        {/* Body */}
        {view === "week" ? <WeekView start={startOfWeek(cursor)} byDate={byDate} today={today} onSelect={setSelected} /> : <MonthView cursor={cursor} byDate={byDate} today={today} onSelect={setSelected} />}

        {selected && <EventPopover selection={selected} role={role} onClose={() => setSelected(null)} onChanged={reload} />}
        {adding && <CalAddForm defaultDate={ymd(cursor)} onClose={() => setAdding(false)} onSaved={() => { setAdding(false); reload(); }} />}
      </div>
    </div>
  );
}

// Add-event modal — writes to /api/calendar (shared with the mobile app).
function CalAddForm({ defaultDate, onClose, onSaved }) {
  const KINDS = ["WORKOUT","MEAL","CHECKIN","CONSULT","REVIEW","PLAN","REST","ADMIN"];
  const KIND_ICON = { WORKOUT: "🏋", MEAL: "🍽", CHECKIN: "✅", CONSULT: "💬", REVIEW: "📋", PLAN: "🗺", REST: "😴", ADMIN: "✦" };
  const [kind, setKind] = React.useState("WORKOUT");
  const [title, setTitle] = React.useState("");
  const [sub, setSub] = React.useState("");
  const [date, setDate] = React.useState(defaultDate);
  const [time, setTime] = React.useState("");
  const [dur, setDur] = React.useState("");
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState("");

  const save = () => {
    if (!title.trim()) { setErr("Add a title."); return; }
    setBusy(true); setErr("");
    fetch("/api/calendar", {
      method: "POST", credentials: "same-origin",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ kind, title: title.trim(), sub: sub.trim() || undefined, date, time: /^\d{1,2}:\d{2}$/.test(time) ? time : undefined, durationMin: dur ? Number(dur) : undefined }),
    })
      .then(r => r.ok ? r.json() : r.json().then(e => Promise.reject(e)))
      .then(() => onSaved())
      .catch(e => { setErr((e && e.error) || "Could not save."); setBusy(false); });
  };

  const field = { width: "100%", background: "rgba(242,237,228,0.06)", color: INK, border: "1px solid rgba(242,237,228,0.15)", borderRadius: 10, padding: "11px 12px", fontFamily: sans, fontSize: 14, outline: "none", boxSizing: "border-box" };

  return (
    <div onClick={onClose} style={{ position: "absolute", inset: 0, background: "rgba(10,10,8,0.72)", display: "flex", alignItems: "center", justifyContent: "center", padding: 20, zIndex: 10 }}>
      <div onClick={e => e.stopPropagation()} style={{ width: "min(440px, 100%)", background: PAPER, color: INK, border: "1px solid rgba(242,237,228,0.12)", borderRadius: 14, padding: 22, fontFamily: sans }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 16 }}>
          <div style={{ fontFamily: serif, fontSize: 22, letterSpacing: "-0.02em" }}>Add to calendar</div>
          <button onClick={onClose} style={{ background: "transparent", color: "rgba(242,237,228,0.6)", border: 0, fontSize: 20, cursor: "pointer" }}>×</button>
        </div>
        <div style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 9, letterSpacing: "0.2em", textTransform: "uppercase", color: "rgba(242,237,228,0.5)", marginBottom: 9, fontWeight: 700 }}>Type</div>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 7, marginBottom: 18 }}>
          {KINDS.map(k => {
            const on = kind === k;
            return (
              <button key={k} onClick={() => setKind(k)} style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 4, padding: "10px 2px 7px", borderRadius: 12, cursor: "pointer", border: `1px solid ${on ? TEAL : "rgba(242,237,228,0.15)"}`, background: on ? "rgba(10,197,168,0.13)" : "transparent" }}>
                <span style={{ fontSize: 17, lineHeight: 1, filter: on ? "none" : "grayscale(0.4)" }}>{KIND_ICON[k] || "✦"}</span>
                <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 8, fontWeight: 700, letterSpacing: "0.04em", color: on ? INK : "rgba(242,237,228,0.55)" }}>{k}</span>
              </button>
            );
          })}
        </div>
        <div style={{ display: "grid", gap: 10 }}>
          <input value={title} onChange={e => setTitle(e.target.value)} placeholder="Title" style={field} />
          <input value={sub} onChange={e => setSub(e.target.value)} placeholder="Details (optional)" style={field} />
          <div style={{ display: "grid", gridTemplateColumns: "1.4fr 1fr 0.8fr", gap: 10 }}>
            <input type="date" value={date} onChange={e => setDate(e.target.value)} style={{ ...field, colorScheme: "dark" }} />
            <input value={time} onChange={e => setTime(e.target.value)} placeholder="HH:MM" style={field} />
            <input value={dur} onChange={e => setDur(e.target.value)} placeholder="min" type="number" style={field} />
          </div>
        </div>
        {err && <div style={{ color: "#ff8b7f", fontSize: 12, marginTop: 10, fontFamily: "'JetBrains Mono', monospace", letterSpacing: "0.04em" }}>{err}</div>}
        <button onClick={save} disabled={busy} style={{ width: "100%", marginTop: 18, padding: "15px 0", borderRadius: 999, background: TEAL, color: "#031f1c", border: 0, fontFamily: "'JetBrains Mono', monospace", fontSize: 12, fontWeight: 800, letterSpacing: "0.16em", textTransform: "uppercase", cursor: busy ? "wait" : "pointer", opacity: busy ? 0.65 : 1 }}>{busy ? "Saving…" : "Add to calendar →"}</button>
      </div>
    </div>
  );
}

const navArrowStyle = { background: "transparent", color: INK, border: "1px solid rgba(242,237,228,0.15)", width: 32, height: 32, borderRadius: 999, fontFamily: sans, fontSize: 16, cursor: "pointer", display: "inline-flex", alignItems: "center", justifyContent: "center", lineHeight: 1 };

function legendForRole(role) {
  if (role === "trainer") return ["SESSION","CHECKIN","REVIEW","ADMIN"];
  if (role === "nutritionist") return ["CONSULT","PLAN","REVIEW","ADMIN"];
  return ["WORKOUT","MEAL","CHECKIN"];
}

function WeekView({ start, byDate, today, onSelect }) {
  const days = Array.from({length: 7}, (_, i) => addDays(start, i));
  const hours = Array.from({length: 14}, (_, i) => i + 7); // 7am – 8pm
  return (
    <div style={{ padding: "0 28px 28px" }}>
      <div style={{ display: "grid", gridTemplateColumns: "56px repeat(7, 1fr)", borderTop: "1px solid rgba(242,237,228,0.08)", borderLeft: "1px solid rgba(242,237,228,0.08)" }}>
        <div />
        {days.map(d => {
          const isToday = ymd(d) === ymd(today);
          return (
            <div key={d.toString()} style={{ padding: "14px 12px", borderRight: "1px solid rgba(242,237,228,0.08)", borderBottom: "1px solid rgba(242,237,228,0.08)", background: isToday ? "rgba(10,197,168,0.06)" : "transparent" }}>
              <div style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 10.5, letterSpacing: "0.14em", color: isToday ? TEAL_BRIGHT : "rgba(242,237,228,0.55)" }}>{d.toLocaleDateString("en-US", { weekday: "short" }).toUpperCase()}</div>
              <div style={{ fontFamily: serif, fontSize: 22, marginTop: 4, color: isToday ? INK : "rgba(242,237,228,0.8)" }}>{d.getDate()}</div>
            </div>
          );
        })}

        {hours.map(h => (
          <React.Fragment key={h}>
            <div style={{ padding: "8px 8px 0", borderRight: "1px solid rgba(242,237,228,0.08)", borderBottom: "1px solid rgba(242,237,228,0.05)", fontFamily: "'JetBrains Mono', monospace", fontSize: 10, color: "rgba(242,237,228,0.4)", minHeight: 54 }}>
              {h === 12 ? "12 PM" : h < 12 ? `${h} AM` : `${h-12} PM`}
            </div>
            {days.map(d => {
              const list = (byDate[ymd(d)] || []).filter(e => e.time && Number(e.time.slice(0,2)) === h);
              return (
                <div key={d.toString()+h} style={{ position: "relative", borderRight: "1px solid rgba(242,237,228,0.08)", borderBottom: "1px solid rgba(242,237,228,0.05)", minHeight: 54, padding: 4 }}>
                  {list.map((e, i) => (
                    <EventChip key={i} e={e} onSelect={onSelect} />
                  ))}
                </div>
              );
            })}
          </React.Fragment>
        ))}
      </div>
    </div>
  );
}

function EventChip({ e, compact, onSelect }) {
  const color = KIND_COLORS[e.kind] || TEAL;
  const handle = (ev) => {
    ev.stopPropagation();
    if (!onSelect) return;
    const r = ev.currentTarget.getBoundingClientRect();
    onSelect({ event: e, anchor: { x: r.left, y: r.top, w: r.width, h: r.height } });
  };
  return (
    <button type="button" onClick={handle} title={`${e.title}${e.sub ? " — " + e.sub : ""}`}
      style={{ display: "block", width: "100%", textAlign: "left", background: "rgba(242,237,228,0.04)", borderLeft: `3px solid ${color}`, borderTop: 0, borderRight: 0, borderBottom: 0, padding: compact ? "3px 6px" : "6px 8px", borderRadius: 3, marginBottom: 3, cursor: "pointer", overflow: "hidden", fontFamily: "inherit", color: "inherit" }}
      onMouseEnter={(ev) => { ev.currentTarget.style.background = "rgba(242,237,228,0.08)"; }}
      onMouseLeave={(ev) => { ev.currentTarget.style.background = "rgba(242,237,228,0.04)"; }}>
      <div style={{ display: "flex", gap: 6, alignItems: "baseline" }}>
        {e.time && <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 9.5, color: "rgba(242,237,228,0.55)" }}>{e.time}</span>}
        <span style={{ fontSize: compact ? 10.5 : 12, fontWeight: 500, color: INK, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{e.title}</span>
      </div>
      {!compact && e.sub && <div style={{ fontSize: 10.5, color: "rgba(242,237,228,0.5)", marginTop: 1, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{e.sub}</div>}
    </button>
  );
}

function MonthView({ cursor, byDate, today, onSelect }) {
  const first = new Date(cursor.getFullYear(), cursor.getMonth(), 1);
  const gridStart = startOfWeek(first);
  const weeks = 6;
  const cells = Array.from({length: weeks*7}, (_, i) => addDays(gridStart, i));
  const weekdays = ["MON","TUE","WED","THU","FRI","SAT","SUN"];
  return (
    <div style={{ padding: "0 28px 28px" }}>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(7,1fr)", borderBottom: "1px solid rgba(242,237,228,0.08)" }}>
        {weekdays.map(w => <div key={w} style={{ padding: "12px 14px", fontFamily: "'JetBrains Mono', monospace", fontSize: 10.5, letterSpacing: "0.14em", color: "rgba(242,237,228,0.55)" }}>{w}</div>)}
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(7,1fr)", borderLeft: "1px solid rgba(242,237,228,0.08)" }}>
        {cells.map((d, i) => {
          const inMonth = d.getMonth() === cursor.getMonth();
          const isToday = ymd(d) === ymd(today);
          const list = byDate[ymd(d)] || [];
          return (
            <div key={i} style={{ minHeight: 112, borderRight: "1px solid rgba(242,237,228,0.08)", borderBottom: "1px solid rgba(242,237,228,0.08)", padding: 8, background: isToday ? "rgba(10,197,168,0.06)" : "transparent", opacity: inMonth ? 1 : 0.35 }}>
              <div style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 11, color: isToday ? TEAL_BRIGHT : "rgba(242,237,228,0.7)", marginBottom: 4 }}>{d.getDate()}</div>
              {list.slice(0,3).map((e, j) => <EventChip key={j} e={e} compact onSelect={onSelect} />)}
              {list.length > 3 && <div style={{ fontSize: 10.5, color: "rgba(242,237,228,0.5)", paddingLeft: 4, marginTop: 2 }}>+{list.length-3} more</div>}
            </div>
          );
        })}
      </div>
    </div>
  );
}

function actionsForEvent(role, kind) {
  // Returns [{ label, primary? }]
  if (role === "client") {
    if (kind === "WORKOUT") return [{ label: "Start workout", primary: true }, { label: "View plan" }, { label: "Reschedule" }];
    if (kind === "MEAL")    return [{ label: "Log meal", primary: true }, { label: "Swap meal" }, { label: "See macros" }];
    if (kind === "CHECKIN") return [{ label: "Join call", primary: true }, { label: "Message coach" }, { label: "Reschedule" }];
  }
  if (role === "trainer") {
    if (kind === "SESSION") return [{ label: "Join session", primary: true }, { label: "Open client" }, { label: "Reschedule" }];
    if (kind === "CHECKIN") return [{ label: "Join check-in", primary: true }, { label: "Open notes" }, { label: "Reschedule" }];
    if (kind === "REVIEW")  return [{ label: "Open review", primary: true }, { label: "Message client" }];
    if (kind === "ADMIN")   return [{ label: "Mark done", primary: true }, { label: "Edit" }];
  }
  if (role === "nutritionist") {
    if (kind === "CONSULT") return [{ label: "Join consult", primary: true }, { label: "Open client" }, { label: "Reschedule" }];
    if (kind === "PLAN")    return [{ label: "Open plan", primary: true }, { label: "Send to client" }];
    if (kind === "REVIEW")  return [{ label: "Open review", primary: true }, { label: "Message client" }];
    if (kind === "ADMIN")   return [{ label: "Mark done", primary: true }, { label: "Edit" }];
  }
  return [{ label: "View details", primary: true }];
}

function fmtTimeRange(time, duration) {
  if (!time) return "All day";
  const [h, m] = time.split(":").map(Number);
  const start = new Date(2026, 0, 1, h, m);
  const end = new Date(start.getTime() + (duration || 60) * 60000);
  const fmt = (d) => {
    const hh = d.getHours();
    const mm = d.getMinutes().toString().padStart(2, "0");
    const ap = hh >= 12 ? "PM" : "AM";
    const hh12 = ((hh + 11) % 12) + 1;
    return `${hh12}:${mm} ${ap}`;
  };
  return `${fmt(start)} – ${fmt(end)}`;
}

function fmtLongDate(dateStr) {
  const d = new Date(dateStr + "T00:00:00");
  return d.toLocaleDateString("en-US", { weekday: "long", month: "long", day: "numeric" });
}

function EventPopover({ selection, role, onClose, onChanged }) {
  const { event: e, anchor } = selection;
  const canDelete = e && e.editable && e.source === "event";
  const removeEvent = () => {
    fetch("/api/calendar", { method: "DELETE", credentials: "same-origin", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ id: e.id }) })
      .then(() => { onClose(); if (onChanged) onChanged(); })
      .catch(() => {});
  };
  const popRef = React.useRef(null);
  const [pos, setPos] = React.useState(() => {
    // Initial guess — useLayoutEffect will refine once we know size
    return { left: anchor.x + anchor.w + 10, top: anchor.y - 4 };
  });

  React.useEffect(() => {
    const onKey = (ev) => { if (ev.key === "Escape") onClose(); };
    const onClick = (ev) => { if (popRef.current && !popRef.current.contains(ev.target)) onClose(); };
    window.addEventListener("keydown", onKey);
    const t = setTimeout(() => window.addEventListener("mousedown", onClick), 0);
    return () => { window.removeEventListener("keydown", onKey); clearTimeout(t); window.removeEventListener("mousedown", onClick); };
  }, [onClose]);

  React.useLayoutEffect(() => {
    if (!popRef.current) return;
    const r = popRef.current.getBoundingClientRect();
    const vw = window.innerWidth, vh = window.innerHeight;
    let left = anchor.x + anchor.w + 10;
    let top = anchor.y - 4;
    if (left + r.width > vw - 16) left = Math.max(16, anchor.x - r.width - 10);
    if (left < 16) left = 16;
    if (top + r.height > vh - 16) top = Math.max(16, vh - r.height - 16);
    if (top < 16) top = 16;
    setPos({ left, top });
  }, [anchor.x, anchor.y, anchor.w, anchor.h]);

  const color = KIND_COLORS[e.kind] || TEAL;
  const actions = actionsForEvent(role, e.kind);

  const node = (
    <div ref={popRef}
      onClick={(ev) => ev.stopPropagation()}
      onMouseDown={(ev) => ev.stopPropagation()}
      style={{
        position: "fixed", left: pos.left, top: pos.top,
        width: 320, background: "#1a1612", color: INK,
        border: "1px solid rgba(242,237,228,0.14)", borderRadius: 10,
        boxShadow: "0 24px 60px rgba(0,0,0,0.55)",
        zIndex: 300, overflow: "hidden", fontFamily: sans,
      }}>
      <div style={{ height: 3, background: color }} />
      <div style={{ padding: "16px 18px 6px" }}>
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 10 }}>
          <div style={{ display: "inline-flex", alignItems: "center", gap: 8, fontFamily: "'JetBrains Mono', monospace", fontSize: 10.5, letterSpacing: "0.14em", color }}>
            <span style={{ width: 8, height: 8, borderRadius: 2, background: color }} />
            {(KIND_LABEL[e.kind] || e.kind || "").toUpperCase()}
          </div>
          <button onClick={onClose} aria-label="Close" style={{ background: "transparent", color: "rgba(242,237,228,0.5)", border: 0, fontSize: 18, padding: "0 4px", cursor: "pointer", lineHeight: 1 }}>×</button>
        </div>
        <div style={{ fontFamily: serif, fontSize: 22, letterSpacing: "-0.02em", lineHeight: 1.15, marginBottom: 8 }}>{e.title}</div>
        <div style={{ fontSize: 12.5, color: "rgba(242,237,228,0.65)", marginBottom: 4 }}>{fmtLongDate(e.date)}</div>
        <div style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 11, color: "rgba(242,237,228,0.55)" }}>{fmtTimeRange(e.time, e.duration != null ? e.duration : e.durationMin)}</div>
      </div>

      {(e.sub || e.with || e.location) && (
        <div style={{ padding: "12px 18px", borderTop: "1px solid rgba(242,237,228,0.08)", display: "grid", gap: 6 }}>
          {e.with && (
            <div style={{ display: "flex", gap: 10, fontSize: 12.5 }}>
              <span style={{ color: "rgba(242,237,228,0.5)", width: 56, flexShrink: 0 }}>With</span>
              <span>{e.with}</span>
            </div>
          )}
          {e.location && (
            <div style={{ display: "flex", gap: 10, fontSize: 12.5 }}>
              <span style={{ color: "rgba(242,237,228,0.5)", width: 56, flexShrink: 0 }}>Where</span>
              <span>{e.location}</span>
            </div>
          )}
          {e.sub && (
            <div style={{ display: "flex", gap: 10, fontSize: 12.5 }}>
              <span style={{ color: "rgba(242,237,228,0.5)", width: 56, flexShrink: 0 }}>Details</span>
              <span style={{ color: "rgba(242,237,228,0.85)" }}>{e.sub}</span>
            </div>
          )}
        </div>
      )}

      <div style={{ padding: "12px 14px 14px", borderTop: "1px solid rgba(242,237,228,0.08)", display: "flex", flexWrap: "wrap", gap: 8 }}>
        {canDelete && (
          <button onClick={removeEvent}
            style={{ background: "transparent", color: "#ff8b7f", border: "1px solid rgba(255,139,127,0.4)", padding: "9px 14px", borderRadius: 4, fontFamily: sans, fontSize: 12, cursor: "pointer" }}>
            Delete
          </button>
        )}
        {actions.map((a, i) => (
          a.primary ? (
            <button key={i} onClick={onClose}
              style={{ background: TEAL, color: PAPER, border: 0, padding: "9px 14px", borderRadius: 4, fontFamily: sans, fontSize: 12, fontWeight: 500, letterSpacing: "0.03em", cursor: "pointer" }}>
              {a.label} <span style={{ marginLeft: 4 }}>→</span>
            </button>
          ) : (
            <button key={i} onClick={onClose}
              style={{ background: "transparent", color: "rgba(242,237,228,0.85)", border: "1px solid rgba(242,237,228,0.18)", padding: "9px 14px", borderRadius: 4, fontFamily: sans, fontSize: 12, cursor: "pointer" }}>
              {a.label}
            </button>
          )
        ))}
      </div>
    </div>
  );
  return ReactDOM.createPortal(node, document.body);
}

Object.assign(window, { CalendarOverlay, useCalendarOverlay });

// ═══════════════════════════════════════════════════════════
// HOME CARD STACK — customizable, live (mirrors the mobile app)
// Six card types from /api/client/analytics, each KICKER · HERO · meta ·
// caption. Pin locks to top; drag reorders; a "Cards ▾" menu toggles
// visibility; unpinned cards auto-order by "most alive today". Layout persists
// to client_home_cards (same store the app uses) so web ↔ mobile stay in sync.
// ═══════════════════════════════════════════════════════════
const SHAPE_CARD_TYPES = ["training", "recovery", "energy", "consistency", "protein", "mood"];
const SHAPE_CARD_LABEL = { training: "Training", recovery: "Recovery", energy: "Energy", consistency: "Consistency", protein: "Protein", mood: "Mood" };
const SHAPE_CARD_DEFAULTS = ["training", "recovery", "energy"];
const SHAPE_CARD_ACCENT = { training: "#e3a544", recovery: "#5b8df9", energy: TEAL, consistency: "#5fb16e", protein: "#e06547", mood: TEAL_BRIGHT };

function shapeBuildCard(type, ticker) {
  const tk = ticker || {};
  const dash = "—";
  const num = (v) => (typeof v === "number" ? v : null);
  const goal = tk.goal_kind || "maintain";
  switch (type) {
    case "training": {
      const dist = num(tk.today_distance_km), mins = num(tk.today_activity_min), at = tk.today_activity_type;
      const cap = (s) => (s ? s.charAt(0).toUpperCase() + s.slice(1) : "");
      if (dist && dist > 0) { const mi = Math.round((dist / 1.609) * 10) / 10; return { kicker: "Training", hero: String(mi), unit: `mi · ${cap(at) || "logged"}`, meta: [cap(at) || "Activity", mins ? `${mins} min` : "today", "in the bank"], caption: "Distance in the bank today. Nice work.", alive: 85 }; }
      if (mins && mins > 0) return { kicker: "Training", hero: String(mins), unit: `min · ${cap(at) || "trained"}`, meta: [cap(at) || "Trained", "today", "logged"], caption: "Session logged today. Stay on the program.", alive: 82 };
      const wk = num(tk.workouts_this_week);
      return { kicker: "Training", hero: wk != null ? String(wk) : dash, unit: wk != null ? (wk === 1 ? "workout this wk" : "workouts this wk") : "this week", meta: ["This week", wk != null ? "logged" : "log a workout", "keep moving"], caption: wk ? "Sessions in the bank. Get today in too." : "Nothing logged yet — get one in today.", alive: wk ? 60 : 38 };
    }
    case "recovery": {
      const sleep = num(tk.sleep_hours), hrv = num(tk.hrv_ms), rec = num(tk.recovery_score);
      const ready = rec != null ? rec >= 66 : (sleep == null ? null : sleep >= 7);
      const mid = rec != null && rec >= 40 && rec < 66;
      const sl = sleep != null ? `${Math.floor(sleep)}h ${Math.round((sleep % 1) * 60)}m` : dash;
      return { kicker: "Recovery", hero: (rec == null && sleep == null) ? dash : (ready ? "Ready" : mid ? "Steady" : "Low"), unit: rec != null ? `${Math.round(rec)}%` : "", meta: ["Sleep", sl, hrv != null ? `HRV ${Math.round(hrv)}` : "HRV —"], caption: (rec == null && sleep == null) ? "Connect a wearable or log sleep to see readiness." : ready ? "Recovered. Body's good to push tomorrow." : mid ? "Middling — train, but listen to the body." : "Under-recovered — keep today easy.", alive: (rec == null && sleep == null) ? 30 : (ready ? 52 : mid ? 72 : 92) };
    }
    case "energy": {
      const cal = num(tk.cal), target = num(tk.cal_target);
      const has = cal != null && target != null;
      const bal = has ? cal - target : null;
      const est = has ? `~${bal < 0 ? "−" : "+"}${Math.abs(bal)} kcal` : "— kcal";
      const map = {
        cut: { kicker: "Energy · On target", hero: has ? (bal < 0 ? "Under" : "Over") : "Open", meta: ["Fat loss", est, "within range"], caption: has ? (bal < 0 ? "You're tracking where you want to be." : "A little over — ease back tomorrow.") : "Log a meal to see where today lands.", accent: "#5fb16e" },
        maintain: { kicker: "Energy · Balanced", hero: has ? "Even" : "Open", meta: ["Maintain", est, "steady"], caption: has ? "Right in the pocket for holding steady." : "Log a meal to see where today lands.", accent: "#5b8df9" },
        build: { kicker: "Energy · Fuel up", hero: has ? (bal < 0 ? "Short" : "Built") : "Open", meta: ["Build", est, bal < 0 ? "under surplus" : "on surplus"], caption: has ? (bal < 0 ? "A build day wants more — add a meal." : "Surplus in. That's the fuel for growth.") : "Log a meal to see where today lands.", accent: "#e3a544" },
      };
      const m = map[goal] || map.maintain;
      return { ...m, unit: "", alive: has ? 60 : 40, accentOverride: m.accent };
    }
    case "consistency": {
      const pts = num(tk.weekly_points);
      return { kicker: "Consistency", hero: pts != null ? `+${pts}` : dash, unit: pts != null ? "pts this wk" : "this week", meta: ["Shape Score", pts != null ? "this week" : "log to earn", "streaks compound"], caption: pts ? "Showing up. Every log adds to the score." : "Log a habit or workout to start the week.", alive: pts ? 50 : 25 };
    }
    case "protein": {
      const p = num(tk.protein_g), target = num(tk.protein_target) || 150;
      const hit = p != null && p >= target * 0.9;
      return { kicker: "Protein", hero: p != null ? String(p) : dash, unit: p != null ? `/ ${target} g` : "g today", meta: ["Today", p != null ? `${Math.round((p / target) * 100)}% of target` : "not logged", "muscle fuel"], caption: p == null ? "Log meals to track protein." : hit ? "On target — protein locked in." : `Behind on protein — ${target - p}g to go.`, alive: p == null ? 35 : (hit ? 45 : 75) };
    }
    case "mood": {
      const m = num(tk.mood);
      const word = m == null ? dash : m >= 8 ? "Great" : m >= 6 ? "Good" : m >= 4 ? "Okay" : "Low";
      return { kicker: "Mood", hero: word, unit: m != null ? `${m}/10` : "check in", meta: ["Today", m != null ? "logged" : "tap to log", "1–10"], caption: m == null ? "How are you feeling? A quick check-in keeps your coach in the loop." : m >= 6 ? "Feeling good today — keep the momentum." : "Off day — be kind to yourself.", alive: m == null ? 30 : (m < 4 ? 80 : 22) };
    }
    default:
      return { kicker: SHAPE_CARD_LABEL[type] || type, hero: dash, unit: "", meta: [], caption: "", alive: 0 };
  }
}

function ShapeHomeCards() {
  const [ticker, setTicker] = React.useState({});
  const [layout, setLayout] = React.useState({ order: SHAPE_CARD_DEFAULTS.slice(), pinned: [], manual: false });
  const [menuOpen, setMenuOpen] = React.useState(false);
  const [dragType, setDragType] = React.useState(null);
  const [overType, setOverType] = React.useState(null);

  React.useEffect(() => {
    fetch("/api/client/analytics", { credentials: "same-origin" })
      .then(r => (r.ok ? r.json() : null))
      .then(d => { if (d && d.ticker) setTicker({ ...d.ticker, workouts_this_week: d.workouts_this_week, weekly_points: d.kpis && d.kpis.weekly_points }); })
      .catch(() => {});
    if (window.shapeDb && window.shapeDb.getUserGoals) {
      window.shapeDb.getUserGoals("client_home_cards").then((s) => {
        if (s && Array.isArray(s.order)) setLayout({ order: s.order.filter(x => SHAPE_CARD_TYPES.includes(x)), pinned: (s.pinned || []).filter(x => s.order.includes(x)), manual: !!s.manual });
      }).catch(() => {});
    }
  }, []);
  const persist = (next) => {
    setLayout(next);
    try { window.shapeDb && window.shapeDb.saveUserGoals && window.shapeDb.saveUserGoals("client_home_cards", next); } catch (e) {}
  };

  const models = {};
  layout.order.forEach(type => { models[type] = shapeBuildCard(type, ticker); });
  const pinnedOrder = layout.pinned.filter(x => layout.order.includes(x));
  const unpinned = layout.order.filter(x => !layout.pinned.includes(x));
  if (!layout.manual) unpinned.sort((a, b) => (models[b].alive - models[a].alive) || (layout.order.indexOf(a) - layout.order.indexOf(b)));
  const ordered = [...pinnedOrder, ...unpinned];

  const togglePin = (type) => persist({ ...layout, pinned: layout.pinned.includes(type) ? layout.pinned.filter(x => x !== type) : [...layout.pinned, type] });
  const hideCard = (type) => persist({ ...layout, order: layout.order.filter(x => x !== type), pinned: layout.pinned.filter(x => x !== type) });
  const toggleVisible = (type) => { if (layout.order.includes(type)) return hideCard(type); persist({ ...layout, order: SHAPE_CARD_TYPES.filter(x => layout.order.includes(x) || x === type) }); };
  const onDrop = (target) => { if (!dragType || dragType === target) { setDragType(null); setOverType(null); return; } const next = layout.order.filter(x => x !== dragType); const idx = next.indexOf(target); next.splice(idx < 0 ? next.length : idx, 0, dragType); persist({ ...layout, order: next, manual: true }); setDragType(null); setOverType(null); };

  const card = (type) => {
    const m = models[type];
    const accent = m.accentOverride || SHAPE_CARD_ACCENT[type];
    const pinned = layout.pinned.includes(type);
    const draggable = !pinned;
    return (
      <div key={type}
        draggable={draggable}
        onDragStart={draggable ? () => setDragType(type) : undefined}
        onDragEnd={draggable ? () => { setDragType(null); setOverType(null); } : undefined}
        onDragOver={draggable ? (e) => { e.preventDefault(); if (overType !== type) setOverType(type); } : undefined}
        onDrop={draggable ? (e) => { e.preventDefault(); onDrop(type); } : undefined}
        style={{ borderRadius: 14, marginBottom: 16, background: "rgba(242,237,228,0.04)", overflow: "hidden",
          border: (overType === type && dragType && dragType !== type) ? `1.5px dashed ${accent}` : (pinned ? `1.5px solid ${accent}` : "1px solid rgba(242,237,228,0.1)"),
          opacity: dragType === type ? 0.5 : 1 }}>
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "16px 22px 0" }}>
          <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 11, fontWeight: 700, letterSpacing: "0.18em", textTransform: "uppercase", color: accent, display: "inline-flex", alignItems: "center", gap: 10 }}>
            {draggable && <span title="Drag to reorder" style={{ cursor: "grab", color: "rgba(242,237,228,0.4)" }}>⠿</span>}
            {m.kicker}{pinned ? " · pinned" : ""}
          </span>
          <span style={{ display: "inline-flex", gap: 6 }}>
            <button onClick={() => togglePin(type)} title={pinned ? "Unpin" : "Pin"} style={shapeCardBtn(pinned ? accent : "rgba(242,237,228,0.5)")}>⌃</button>
            <button onClick={() => hideCard(type)} title="Hide" style={shapeCardBtn("rgba(242,237,228,0.5)")}>×</button>
          </span>
        </div>
        <div style={{ padding: "8px 22px 22px" }}>
          <div style={{ display: "flex", alignItems: "baseline", gap: 10, flexWrap: "wrap" }}>
            <span style={{ fontFamily: serif, fontSize: 64, letterSpacing: "-0.035em", color: INK, lineHeight: 0.9 }}>{m.hero}</span>
            {m.unit ? <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 12, letterSpacing: "0.1em", textTransform: "uppercase", color: "rgba(242,237,228,0.5)" }}>{m.unit}</span> : null}
          </div>
          {m.meta && m.meta.length ? (
            <div style={{ marginTop: 12, display: "flex", gap: 10, flexWrap: "wrap", fontFamily: "'JetBrains Mono', monospace", fontSize: 11, letterSpacing: "0.08em", textTransform: "uppercase", color: "rgba(242,237,228,0.5)" }}>
              {m.meta.filter(Boolean).map((s, i) => <React.Fragment key={i}>{i > 0 && <span style={{ opacity: 0.5 }}>·</span>}<span>{s}</span></React.Fragment>)}
            </div>
          ) : null}
          {m.caption ? <div style={{ marginTop: 14, fontFamily: sans, fontSize: 16, color: "rgba(242,237,228,0.72)", lineHeight: 1.35 }}>{m.caption}</div> : null}
        </div>
      </div>
    );
  };

  return (
    <div>
      <div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between", marginBottom: 18 }}>
        <div>
          <h2 style={{ fontFamily: serif, fontSize: 36, letterSpacing: "-0.02em", fontWeight: 400, margin: 0 }}>{["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"][new Date().getDay()]}.</h2>
          <div style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 11, letterSpacing: "0.18em", textTransform: "uppercase", color: "rgba(242,237,228,0.5)", marginTop: 6 }}>Your stack · pin, drag, or choose cards</div>
        </div>
        <div style={{ position: "relative" }}>
          <button onClick={() => setMenuOpen(v => !v)} style={{ padding: "9px 16px", borderRadius: 999, border: `1px solid ${menuOpen ? TEAL : "rgba(242,237,228,0.2)"}`, background: menuOpen ? TEAL : "transparent", color: menuOpen ? "#0a0f0d" : INK, fontFamily: "'JetBrains Mono', monospace", fontSize: 11, fontWeight: 700, letterSpacing: "0.1em", cursor: "pointer" }}>CARDS ▾</button>
          {menuOpen && (
            <>
              <div onClick={() => setMenuOpen(false)} style={{ position: "fixed", inset: 0, zIndex: 50 }} />
              <div style={{ position: "absolute", top: "calc(100% + 8px)", right: 0, zIndex: 51, width: 240, background: "#1a1612", border: "1px solid rgba(242,237,228,0.15)", borderRadius: 12, overflow: "hidden", boxShadow: "0 24px 60px rgba(0,0,0,0.5)" }}>
                <div style={{ padding: "12px 16px", borderBottom: "1px solid rgba(242,237,228,0.08)", fontFamily: "'JetBrains Mono', monospace", fontSize: 10, letterSpacing: "0.18em", textTransform: "uppercase", color: "rgba(242,237,228,0.5)" }}>Show on home</div>
                {SHAPE_CARD_TYPES.map(type => {
                  const on = layout.order.includes(type);
                  return (
                    <button key={type} onClick={() => toggleVisible(type)} style={{ width: "100%", display: "flex", alignItems: "center", justifyContent: "space-between", padding: "13px 16px", border: 0, borderTop: "1px solid rgba(242,237,228,0.06)", background: "transparent", cursor: "pointer" }}>
                      <span style={{ display: "inline-flex", alignItems: "center", gap: 10 }}>
                        <span style={{ width: 9, height: 9, borderRadius: 2, background: SHAPE_CARD_ACCENT[type] }} />
                        <span style={{ fontFamily: sans, fontSize: 14, color: INK }}>{SHAPE_CARD_LABEL[type]}</span>
                      </span>
                      <span style={{ width: 36, height: 20, borderRadius: 999, padding: 2, border: `1px solid ${on ? SHAPE_CARD_ACCENT[type] : "rgba(242,237,228,0.2)"}`, background: on ? SHAPE_CARD_ACCENT[type] : "transparent", display: "inline-flex", alignItems: "center", justifyContent: on ? "flex-end" : "flex-start" }}>
                        <span style={{ width: 14, height: 14, borderRadius: 999, background: on ? "#0a0f0d" : "rgba(242,237,228,0.5)", display: "block" }} />
                      </span>
                    </button>
                  );
                })}
              </div>
            </>
          )}
        </div>
      </div>
      {ordered.length === 0 && <div style={{ padding: "24px", borderRadius: 14, border: "1px dashed rgba(242,237,228,0.15)", fontFamily: sans, fontSize: 15, color: "rgba(242,237,228,0.5)" }}>No cards. Tap <b style={{ color: INK }}>Cards ▾</b> to choose what to show.</div>}
      {ordered.map(card)}
      <div style={{ fontFamily: sans, fontSize: 13, color: "rgba(242,237,228,0.45)", lineHeight: 1.5 }}>
        <b style={{ color: "rgba(242,237,228,0.7)" }}>Three defaults, then it's yours.</b> Drag ⠿ to reorder, pin to lock to top, or choose cards from Cards ▾.
        {layout.manual ? <> Order is yours — <a href="#" onClick={(e) => { e.preventDefault(); persist({ ...layout, manual: false }); }} style={{ color: TEAL_BRIGHT }}>switch to auto</a>.</> : " Unpinned cards reorder by what's most alive that day."}
      </div>
    </div>
  );
}
function shapeCardBtn(color) { return { width: 28, height: 28, borderRadius: 7, border: 0, background: "transparent", color, fontFamily: "'JetBrains Mono', monospace", fontSize: 14, fontWeight: 800, cursor: "pointer", lineHeight: 1 }; }

Object.assign(window, { ShapeHomeCards });
