// home.jsx — Signed-in Herely home page.

const { useEffect, useMemo, useRef, useState } = React;

// Polyfill BarcodeDetector using jsQR for cross-browser support
if (typeof window !== "undefined" && typeof window.BarcodeDetector === "undefined" && typeof window.jsQR !== "undefined") {
  window.BarcodeDetector = class BarcodeDetector {
    constructor() {}
    async detect(source) {
      const canvas = document.createElement("canvas");
      canvas.width = source.width || source.videoWidth;
      canvas.height = source.height || source.videoHeight;
      if (!canvas.width || !canvas.height) return [];
      
      const ctx = canvas.getContext("2d", { willReadFrequently: true });
      ctx.drawImage(source, 0, 0, canvas.width, canvas.height);
      const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
      
      const code = window.jsQR(imageData.data, imageData.width, imageData.height, { inversionAttempts: "dontInvert" });
      
      if (code && code.data) {
        return [{ rawValue: code.data }];
      }
      return [];
    }
  };
}
const HOME_FALLBACK_NEARBY = [
  { id: "studio4", name: "Studio 1 · 2F", place: "North Annex, Berlin", present: 24, open: 9, live: true, moods: ["open", "building", "curious", "focused"], code: "G7-K2-9X", hostInitial: "N" },
  { id: "library", name: "Coworking 1", place: "Ground floor · Quiet", present: 11, open: 2, live: true, moods: ["focused", "curious", "focused"], code: "L4-B1-2P", hostInitial: "C" },
  { id: "kitchen", name: "Coworking 2", place: "5F · Coffee on", present: 7, open: 5, live: true, moods: ["open", "social", "social"], code: "K3-M9-7R", hostInitial: "K" }
];

const HOME_FALLBACK_RECENT = [
  { id: "salon", name: "Studio 2", place: "Atelier Nord · last night", present: 0, open: 0, live: false, moods: ["curious", "building", "open"], when: "yesterday · 9:42pm" },
  { id: "lab", name: "Coworking 3", place: "Open studio · last week", present: 0, open: 0, live: false, moods: ["building", "focused"], when: "6 days ago" },
  { id: "house", name: "Studio 3", place: "Friday gathering", present: 0, open: 0, live: false, moods: ["social", "open", "curious"], when: "2 weeks ago" }
];

const HOME_FALLBACK_SUMMARY = { present: 24, open: 9, mutual: 2, roomId: "studio4" };
const HOME_SESSION_KEY = "herely.session";
const HOME_PENDING_KEY = "herely.pendingUser";
const HOME_ACTIVE_ROOM_KEY = "herely.activeRoomId";
const HOME_MOOD_KEY = "herely.userMood";
const HOME_API_BASE = window.API_URL || "http://localhost:3001/api";

const HOME_API = {
  async requestJson(url, options, fallback) {
    try {
      const res = await fetch(url, options);
      if (!res.ok) throw new Error("HTTP " + res.status);
      return await res.json();
    } catch (error) {
      return typeof fallback === "function" ? fallback() : fallback;
    }
  },
  getNearbyRooms() {
    return this.requestJson(HOME_API_BASE + "/rooms/nearby", {}, HOME_FALLBACK_NEARBY);
  },
  getRecentRooms() {
    return this.requestJson(HOME_API_BASE + "/rooms/recent", {}, HOME_FALLBACK_RECENT);
  },
  joinRoom(code) {
    return this.requestJson(HOME_API_BASE + "/rooms/join", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ code })
    }, function () {
      const room = HOME_FALLBACK_NEARBY.find((item) => item.code === String(code).toUpperCase());
      if (!room) throw new Error("Room not found");
      return { id: room.id };
    });
  },
  leaveRoom(roomId) {
    return this.requestJson(HOME_API_BASE + "/rooms/" + roomId + "/leave", { method: "POST" }, { success: true });
  },
  getSummary(roomId) {
    return this.requestJson(HOME_API_BASE + "/rooms/" + roomId + "/summary", {}, HOME_FALLBACK_SUMMARY);
  },
  updateMood(mood) {
    return this.requestJson(HOME_API_BASE + "/me/mood", {
      method: "PATCH",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ mood })
    }, { success: true });
  }
};

function useReveal() {
  useEffect(function () {
    const els = document.querySelectorAll(".rv");
    requestAnimationFrame(function () {
      requestAnimationFrame(function () {
        els.forEach(function (el) { el.classList.add("in"); });
      });
    });
  }, []);
}

const Arrow = () => (
  <svg className="arrow" viewBox="0 0 14 14" fill="none">
    <path d="M2 7h10M8 3l4 4-4 4" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" />
  </svg>
);

const SunIcon = () => (
  <svg viewBox="0 0 16 16" width="15" height="15" fill="none">
    <circle cx="8" cy="8" r="3" stroke="currentColor" strokeWidth="1.3" />
    {[0, 45, 90, 135, 180, 225, 270, 315].map((a) => {
      const rad = (a * Math.PI) / 180;
      const x1 = 8 + Math.cos(rad) * 5;
      const y1 = 8 + Math.sin(rad) * 5;
      const x2 = 8 + Math.cos(rad) * 7;
      const y2 = 8 + Math.sin(rad) * 7;
      return <line key={a} x1={x1} y1={y1} x2={x2} y2={y2} stroke="currentColor" strokeWidth="1.3" strokeLinecap="round" />;
    })}
  </svg>
);

const MoonIcon = () => (
  <svg viewBox="0 0 16 16" width="15" height="15" fill="none">
    <path d="M12.5 9.5A5 5 0 1 1 6.5 3.5a4 4 0 0 0 6 6Z" stroke="currentColor" strokeWidth="1.3" strokeLinejoin="round" />
  </svg>
);

const CloseIcon = () => (
  <svg viewBox="0 0 16 16" width="13" height="13" fill="none">
    <path d="M3 3l10 10M13 3L3 13" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" />
  </svg>
);

const QRIcon = ({ size = 18 }) => (
  <svg viewBox="0 0 24 24" width={size} height={size} fill="none">
    <rect x="3" y="3" width="7" height="7" rx="1.5" stroke="currentColor" strokeWidth="1.4" />
    <rect x="14" y="3" width="7" height="7" rx="1.5" stroke="currentColor" strokeWidth="1.4" />
    <rect x="3" y="14" width="7" height="7" rx="1.5" stroke="currentColor" strokeWidth="1.4" />
    <rect x="6" y="6" width="1.5" height="1.5" fill="currentColor" />
    <rect x="17" y="6" width="1.5" height="1.5" fill="currentColor" />
    <rect x="6" y="17" width="1.5" height="1.5" fill="currentColor" />
    <path d="M14 14h3M14 18h2M19 14v3M14 21h7M17 17h4" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" />
  </svg>
);

const KeyIcon = ({ size = 18 }) => (
  <svg viewBox="0 0 24 24" width={size} height={size} fill="none">
    <circle cx="8" cy="14" r="4" stroke="currentColor" strokeWidth="1.4" />
    <path d="M11 12l9-9M16 8l3 3M19 5l2 2" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" />
  </svg>
);

const PlusIcon = ({ size = 14 }) => (
  <svg viewBox="0 0 16 16" width={size} height={size} fill="none">
    <path d="M8 3v10M3 8h10" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" />
  </svg>
);

const CheckIcon = ({ size = 14 }) => (
  <svg viewBox="0 0 16 16" width={size} height={size} fill="none">
    <path d="M3 8.5l3.5 3.5L13 5" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" />
  </svg>
);

function readSession() {
  try {
    return JSON.parse(localStorage.getItem(HOME_SESSION_KEY) || "null");
  } catch (error) {
    return null;
  }
}

function readPendingUser() {
  try {
    return JSON.parse(localStorage.getItem(HOME_PENDING_KEY) || "null");
  } catch (error) {
    return null;
  }
}

function initials(name) {
  return String(name || "A").trim().charAt(0).toUpperCase() || "A";
}

function firstName(name) {
  return String(name || "Anya").trim().split(/\s+/)[0] || "Anya";
}

function formatInvite(roomId) {
  const url = new URL(window.location.origin);
  url.pathname = "/room/" + roomId;
  return url.toString();
}

const SettingsIcon = () => (
  <svg viewBox="0 0 18 18" width="15" height="15" fill="none">
    <circle cx="9" cy="9" r="2.4" stroke="currentColor" strokeWidth="1.3"/>
    <path d="M9 1.6v1.8M9 14.6v1.8M14.2 3.8l-1.27 1.27M5.07 12.93L3.8 14.2M16.4 9h-1.8M3.4 9H1.6M14.2 14.2l-1.27-1.27M5.07 5.07L3.8 3.8" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round"/>
  </svg>
);
const BellIcon = () => (
  <svg viewBox="0 0 18 18" width="15" height="15" fill="none">
    <path d="M9 2.4c-2.6 0-4.4 1.9-4.4 4.4v2.7L3.4 11.4h11.2l-1.2-1.9V6.8c0-2.5-1.8-4.4-4.4-4.4Z" stroke="currentColor" strokeWidth="1.3" strokeLinejoin="round"/>
    <path d="M7.5 13.5a1.5 1.5 0 0 0 3 0" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round"/>
  </svg>
);
const ShieldIcon = () => (
  <svg viewBox="0 0 18 18" width="15" height="15" fill="none">
    <path d="M9 2L3.6 4.2v4.2c0 3.4 2.3 6.4 5.4 7.4 3.1-1 5.4-4 5.4-7.4V4.2L9 2Z" stroke="currentColor" strokeWidth="1.3" strokeLinejoin="round"/>
  </svg>
);
const SparkIcon = () => (
  <svg viewBox="0 0 18 18" width="15" height="15" fill="none">
    <path d="M9 2.4v3.2M9 12.4v3.2M2.4 9h3.2M12.4 9h3.2M4.5 4.5l2.2 2.2M11.3 11.3l2.2 2.2M4.5 13.5l2.2-2.2M11.3 6.7l2.2-2.2" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round"/>
  </svg>
);
const LogoutIcon = () => (
  <svg viewBox="0 0 18 18" width="15" height="15" fill="none">
    <path d="M7.5 2.4H4.2A1.4 1.4 0 0 0 2.8 3.8v10.4a1.4 1.4 0 0 0 1.4 1.4h3.3" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round"/>
    <path d="M11.5 12L15 9l-3.5-3M15 9H7" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round" strokeLinejoin="round"/>
  </svg>
);
const ChevronRight = () => (
  <svg className="arr" viewBox="0 0 14 14" width="14" height="14" fill="none">
    <path d="M5 3l4 4-4 4" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round"/>
  </svg>
);

/* ─── Profile Drawer ─── */
function ProfileDrawer({ open, onClose, userName, mood, onChangeMood, currentRoomActive, onLeave, onOpenSettings, onSignOut }) {
  const initial = (userName || "A").trim().charAt(0).toUpperCase();
  const moodObj = MOODS.find(m => m.key === mood) || MOODS[0];
  const handle = `@${(userName || "anya").toLowerCase().replace(/\s+/g, "")}`;

  // Local toggles — visible only inside the drawer
  const [ghost, setGhost] = useState(false);
  const [mutual, setMutual] = useState(true);
  const [notify, setNotify] = useState(true);

  useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape" && open) onClose(); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [open, onClose]);

  // Lock body scroll while open
  useEffect(() => {
    if (!open) return;
    const prev = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    return () => { document.body.style.overflow = prev; };
  }, [open]);

  const cycleMood = () => {
    const idx = MOODS.findIndex(m => m.key === mood);
    const next = MOODS[(idx + 1) % MOODS.length];
    onChangeMood(next.key);
  };

  return (
    <>
      <div className={"pd-back " + (open ? "in" : "")} onClick={onClose} aria-hidden={!open}></div>
      <aside className={"pd " + (open ? "in" : "")} role="dialog" aria-label="Your profile" aria-hidden={!open}>
        <div className="pd-head">
          <span className="eyebrow">Your profile</span>
          <button className="pd-close" onClick={onClose} aria-label="Close profile"><CloseIcon/></button>
        </div>

        <div className="pd-body">
          {/* Identity */}
          <div className="pd-id">
            <div className="row">
              <div className="pd-av" style={{ "--mc": moodObj.color }}>
                {initial}
                <span className="ring"></span>
                <span className="live-dot" title={`Signal · ${moodObj.label.toLowerCase()}`}></span>
              </div>
              <div style={{minWidth:0, flex:1}}>
                <div className="name">{userName}<span className="ed">.</span></div>
                <div className="handle">{handle} · Berlin</div>
              </div>
            </div>

            <div className="pd-mood" style={{ "--mc": moodObj.color }}>
              <span className="sw"></span>
              <div className="txt">
                <div className="l1">You · {moodObj.label.toLowerCase()}</div>
                <div className="l2">{moodObj.desc}</div>
              </div>
              <button className="edit" onClick={cycleMood}>Change</button>
            </div>
          </div>

          {/* Where */}
          {currentRoomActive ? (
            <div className="pd-sec">
              <div className="head">
                <span className="lab">Currently in</span>
                <span className="aux">Live</span>
              </div>
              <div className="pd-where">
                <span className="d"></span>
                <div className="col">
                  <div className="l1">Studio 4 · 2F</div>
                  <div className="l2">North Annex, Berlin · 24 present</div>
                </div>
                <button className="lk" onClick={() => { onLeave(); onClose(); }}>Leave</button>
              </div>
            </div>
          ) : (
            <div className="pd-sec">
              <div className="head"><span className="lab">Not in a room</span></div>
              <div className="pd-where">
                <span className="d" style={{background:"var(--mute-2)", boxShadow:"none", animation:"none"}}></span>
                <div className="col">
                  <div className="l1">Standing by</div>
                  <div className="l2">Scan or enter a code to step in</div>
                </div>
              </div>
            </div>
          )}

          {/* Stats */}
          <div className="pd-sec">
            <div className="head">
              <span className="lab">Your texture</span>
              <span className="aux">last 30 days</span>
            </div>
            <div className="pd-stats">
              <div className="cell">
                <div className="v">12</div>
                <div className="l">Rooms</div>
              </div>
              <div className="cell">
                <div className="v">38</div>
                <div className="l">Signals</div>
              </div>
              <div className="cell">
                <div className="v">7</div>
                <div className="l">Mutuals</div>
              </div>
            </div>
          </div>

          {/* Visibility */}
          <div className="pd-sec">
            <div className="head"><span className="lab">Visibility</span></div>
            <div>
              <div className="pd-toggle">
                <div className="col">
                  <div className="l1">Ghost mode</div>
                  <div className="l2">Stay in the room, but don't appear in the radar.</div>
                </div>
                <button className={"pd-switch " + (ghost ? "on" : "")} onClick={() => setGhost(g => !g)} aria-label="Toggle ghost mode" aria-pressed={ghost}></button>
              </div>
              <div className="pd-toggle">
                <div className="col">
                  <div className="l1">Mutual signals</div>
                  <div className="l2">Only show interest when it's reciprocated.</div>
                </div>
                <button className={"pd-switch " + (mutual ? "on" : "")} onClick={() => setMutual(m => !m)} aria-label="Toggle mutual signals" aria-pressed={mutual}></button>
              </div>
              <div className="pd-toggle">
                <div className="col">
                  <div className="l1">Quiet pings</div>
                  <div className="l2">Notify me when a room I've been in wakes up.</div>
                </div>
                <button className={"pd-switch " + (notify ? "on" : "")} onClick={() => setNotify(n => !n)} aria-label="Toggle quiet pings" aria-pressed={notify}></button>
              </div>
            </div>
          </div>

          {/* Links */}
          <div className="pd-sec">
            <div className="head"><span className="lab">Manage</span></div>
            <div className="pd-links">
              <button className="pd-link" onClick={() => onOpenSettings?.("account")}>
                <span className="left">
                  <span className="ic"><SettingsIcon/></span>
                  <span>
                    <div className="ttl">Account & identity</div>
                    <div className="desc">Name, handle, devices</div>
                  </span>
                </span>
                <ChevronRight/>
              </button>
              <button className="pd-link" onClick={() => onOpenSettings?.("notifications")}>
                <span className="left">
                  <span className="ic"><BellIcon/></span>
                  <span>
                    <div className="ttl">Notifications</div>
                    <div className="desc">Pings, mutuals, room wakes</div>
                  </span>
                </span>
                <ChevronRight/>
              </button>
              <button className="pd-link" onClick={() => onOpenSettings?.("privacy")}>
                <span className="left">
                  <span className="ic"><ShieldIcon/></span>
                  <span>
                    <div className="ttl">Privacy & data</div>
                    <div className="desc">What rooms remember about you</div>
                  </span>
                </span>
                <ChevronRight/>
              </button>
              <button className="pd-link" onClick={() => onOpenSettings?.("host")}>
                <span className="left">
                  <span className="ic"><SparkIcon/></span>
                  <span>
                    <div className="ttl">Host a room</div>
                    <div className="desc">Create a code for a space you hold</div>
                  </span>
                </span>
                <ChevronRight/>
              </button>
              <button className="pd-link danger" onClick={onSignOut}>
                <span className="left">
                  <span className="ic"><LogoutIcon/></span>
                  <span>
                    <div className="ttl">Sign out</div>
                    <div className="desc">Step away from Herely</div>
                  </span>
                </span>
                <ChevronRight/>
              </button>
            </div>
          </div>
        </div>

        <div className="pd-foot">
          <span>Member since · Mar 2024</span>
          <span>v0.3</span>
        </div>
      </aside>
    </>
  );
}

/* ─── Nav ─── */
function HomeNav({ mode, onToggle, userName, activeRoomId, onOpenRoom, onProfile, onNavigate }) {
  const midnight = mode === "midnight";
  const hasRoom = !!activeRoomId;
  const goHome = (e) => { e.preventDefault(); if (onNavigate) onNavigate("/"); };
  return (
    <nav className="nav home-nav">
      <div className="wrap nav-inner">
        <BrandLogo href="/" onClick={goHome} />
        <a
          className={"nav-context " + (hasRoom ? "is-link" : "is-hidden")}
          href={hasRoom ? "/room/" + activeRoomId : undefined}
          onClick={(e) => {
            if (!hasRoom) return;
            e.preventDefault();
            if (onOpenRoom) onOpenRoom(activeRoomId);
          }}
          title={hasRoom ? "Currently in Studio 1" : "No active room"}
        >
          <span className="dot"></span>
          <span>Studio 1</span>
          <span className="where">· North Annex, Berlin</span>
        </a>
        <div className="nav-right">
          <button
            className="mode-toggle"
            aria-label={midnight ? "Switch to light mode" : "Switch to midnight mode"}
            title={midnight ? "Light mode" : "Midnight mode"}
            onClick={onToggle}
          >
            {midnight ? <SunIcon /> : <MoonIcon />}
          </button>
          <button className="avatar" aria-label="Profile" title={"Signed in as " + userName} onClick={onProfile}>
            {initials(userName)}
          </button>
        </div>
      </div>
    </nav>
  );
}

function Greeting({ userName, mood, time }) {
  const hr = time.getHours();
  const greet = hr < 5 ? "Still up" : hr < 12 ? "Good morning" : hr < 18 ? "Good afternoon" : "Good evening";
  const timeStr = time.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
  const moodItem = MOODS.find((item) => item.key === mood) || MOODS[0];
  return (
    <header className="greet rv">
      <div className="row1">
        <span className="eyebrow">Home · {timeStr}</span>
        <span className="pill-state" style={{ display: "inline-flex", alignItems: "center", gap: 8 }}>
          <span style={{ width: 7, height: 7, borderRadius: "50%", background: moodColor(mood), boxShadow: "0 0 0 3px " + moodColor(mood) + "33" }}></span>
          You · {moodItem.label.toLowerCase()}
        </span>
      </div>
      <h1>
        {greet}, <span className="ed">{firstName(userName)}.</span>
      </h1>
      <p className="lede">
        The room is quietly waiting. Scan a code, type one in, or step into something you've been in before.
      </p>
    </header>
  );
}

function QRPreview() {
  const pattern = useMemo(() => {
    const grid = Array(49).fill(0);
    const set = (r, c) => { grid[r * 7 + c] = 1; };
    [[0, 0], [0, 1], [1, 0], [1, 1], [0, 5], [0, 6], [1, 5], [1, 6], [5, 0], [5, 1], [6, 0], [6, 1]].forEach(([r, c]) => set(r, c));
    [[2, 3], [3, 2], [3, 3], [3, 5], [4, 1], [4, 4], [2, 5], [5, 4], [6, 5], [5, 6], [2, 6], [6, 3]].forEach(([r, c]) => set(r, c));
    return grid;
  }, []);
  return (
    <div className="qr-preview" aria-hidden>
      {pattern.map((value, index) => <span key={index} className={value ? "" : "off"}></span>)}
    </div>
  );
}

function ScanCard({ onOpen }) {
  return (
    <button className="action-card is-scan" onClick={onOpen} style={{ textAlign: "left", cursor: "pointer", appearance: "none" }}>
      <div className="ah">
        <div className="tag"><span className="dt"></span>Primary · fastest</div>
        <QRPreview />
      </div>
      <h2>Scan a room <span className="ed">code.</span></h2>
      <p className="body">Point your camera at the Herely mark posted in the space. You'll be in before the kettle boils.</p>
      <div className="foot">
        <span className="btn btn-primary" style={{ pointerEvents: "none" }}>
          <QRIcon size={15} /> Open scanner <Arrow />
        </span>
        <span className="mono">Cmd · K</span>
      </div>
    </button>
  );
}

function CodeCard({ onSubmit, disabled }) {
  const [vals, setVals] = useState(["", "", "", "", "", ""]);
  const [err, setErr] = useState(false);
  const [ok, setOk] = useState(false);
  const [shake, setShake] = useState(false);
  const refs = useRef([]);

  const set = (i, value) => {
    const c = String(value).replace(/[^a-z0-9]/gi, "").toUpperCase().slice(0, 1);
    setVals((prev) => {
      const next = prev.slice();
      next[i] = c;
      return next;
    });
    setErr(false);
    if (c && i < 5) refs.current[i + 1]?.focus();
  };

  const code = vals.join("");
  const complete = code.length === 6;

  const submit = async () => {
    if (!complete || ok || disabled) return;
    try {
      setOk(true);
      setErr(false);
      if (onSubmit) await onSubmit(code);
    } catch (error) {
      setOk(false);
      setErr(true);
      setShake(true);
      setTimeout(() => setShake(false), 420);
    }
  };

  const onKey = (i, e) => {
    if (e.key === "Backspace" && !vals[i] && i > 0) {
      refs.current[i - 1]?.focus();
      setVals((prev) => {
        const next = prev.slice();
        next[i - 1] = "";
        return next;
      });
    } else if (e.key === "ArrowLeft" && i > 0) {
      refs.current[i - 1]?.focus();
    } else if (e.key === "ArrowRight" && i < 5) {
      refs.current[i + 1]?.focus();
    } else if (e.key === "Enter") {
      submit();
    }
  };

  const onPaste = (e) => {
    const text = (e.clipboardData?.getData("text") || "").replace(/[^a-z0-9]/gi, "").toUpperCase().slice(0, 6);
    if (!text) return;
    e.preventDefault();
    const next = ["", "", "", "", "", ""];
    for (let index = 0; index < text.length; index += 1) next[index] = text[index];
    setVals(next);
    refs.current[Math.min(text.length, 5)]?.focus();
  };

  return (
    <div className={"action-card is-code " + (shake ? "shake" : "") }>
      <div className="ah">
        <div className="tag"><span className="dt"></span>Have a code? Type it in</div>
        <KeyIcon />
      </div>
      <h2>Enter with a <span className="ed">code.</span></h2>
      <div className="code-input" onPaste={onPaste}>
        {[0, 1, 2].map((i) => (
          <input
            key={i}
            ref={(el) => { refs.current[i] = el; }}
            className={"code-cell " + (vals[i] ? "filled " : "") + (err ? "err" : "")}
            value={vals[i]}
            onChange={(e) => set(i, e.target.value)}
            onKeyDown={(e) => onKey(i, e)}
            inputMode="text"
            autoComplete="off"
            maxLength={1}
            aria-label={"Code character " + (i + 1)}
          />
        ))}
        <span className="code-sep">·</span>
        {[3, 4, 5].map((i) => (
          <input
            key={i}
            ref={(el) => { refs.current[i] = el; }}
            className={"code-cell " + (vals[i] ? "filled " : "") + (err ? "err" : "")}
            value={vals[i]}
            onChange={(e) => set(i, e.target.value)}
            onKeyDown={(e) => onKey(i, e)}
            inputMode="text"
            autoComplete="off"
            maxLength={1}
            aria-label={"Code character " + (i + 1)}
          />
        ))}
      </div>
      <div className={"code-msg " + (err ? "err" : ok ? "ok" : "")}>{err ? "Code not recognized — try again" : ok ? "Match found · joining…" : complete ? "Ready · press enter to join" : "Six characters · letters & numbers"}</div>
      <div className="foot">
        <button className="btn btn-primary" disabled={!complete || ok || disabled} onClick={submit}>
          {ok ? <><CheckIcon /> Joining</> : <>Join room <Arrow /></>}
        </button>
        <span className="mono" title="Try: G7-K2-9X · L4-B1-2P · K3-M9-7R">Try · HERELY</span>
      </div>
    </div>
  );
}

function TinyRadar() {
  const room = useMemo(() => generateRoom(16), []);
  const [time, setTime] = useState(() =>
    new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })
  );
  useEffect(() => {
    const i = setInterval(
      () => setTime(new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })),
      30000
    );
    return () => clearInterval(i);
  }, []);

  return (
    <div className="right mini-room mini-radar" style={{ position: "relative", width: "100%", height: "100%", minHeight: "280px", borderRadius: "20px", overflow: "hidden", border: "1px solid rgba(255,255,255,0.08)", isolation: "isolate", transform: "translateZ(0)" }}>
      <div className="mr-head">
        <div className="mr-now">
          <span className="mr-dot"></span>Studio 1 · 2F · now
        </div>
        <div className="mr-time">{time}</div>
      </div>

      <div style={{ position: "relative", width: "100%", aspectRatio: "1 / 0.92", overflow: "hidden", background: "transparent", isolation: "isolate", transform: "translateZ(0)" }}>
        <SpatialMap
          room={room}
          filter={null}
          fullscreen={false}
          tweaks={useMemo(() => ({ motion: 0.5, glow: 0.6 }), [])}
          selectedId={null}
        />
      </div>

      <div className="mr-foot">
        <div className="mr-leg"><span className="mr-sw" style={{ background: "#FF5E3A" }}></span>{room.counts.lit || 0} lit</div>
        <div className="mr-leg"><span className="mr-sw" style={{ background: "#4DA6FF" }}></span>{room.counts.mellow || 0} mellow</div>
        <div className="mr-leg"><span className="mr-sw" style={{ background: "#FFD700" }}></span>{room.counts.magnetic || 0} magnetic</div>
        <div className="mr-leg"><span className="mr-sw" style={{ background: "#00C49A" }}></span>{room.counts.present || 0} present</div>
      </div>
    </div>
  );
}

function CurrentlyIn({ active, summary, onLeave, onOpenRoom, onShare }) {
  if (!active) {
    return (
      <div className="now-card rv d3" style={{ gridTemplateColumns: "1fr" }}>
        <div className="left">
          <div className="crumbs"><span className="mono">No active room</span></div>
          <div className="title">You're not in a room <span className="ed">yet.</span></div>
          <p className="small" style={{ maxWidth: "42ch" }}>
            Scan or type a code above to step into one. Recent rooms below remember you.
          </p>
        </div>
      </div>
    );
  }
  return (
    <div className="now-card rv d3">
      <div className="left">
        <div className="crumbs">
          <span className="live"><span className="d"></span>You're in</span>
          <span>·</span>
          <span>Studio 1 · 2F · North Annex</span>
        </div>
        <div className="title">A quiet hum, <span className="ed">building energy</span> tonight.</div>
        <div className="stats">
          <div className="s"><div className="v">{summary.present}</div><div className="l">Present</div></div>
          <div className="s"><div className="v">{summary.open}</div><div className="l">Open to talk</div></div>
          <div className="s"><div className="v">{summary.mutual}</div><div className="l">Mutual signals</div></div>
        </div>
        <div className="actions-row">
          <button className="btn btn-primary" onClick={() => onOpenRoom("studio4")}>Open the room <Arrow /></button>
          <button className="btn btn-ghost" onClick={onShare}>Invite someone</button>
          <button className="btn btn-ghost" onClick={onLeave} style={{ borderColor: "transparent", color: "var(--mute)" }}>Leave quietly</button>
        </div>
      </div>
      <TinyRadar />
    </div>
  );
}

function RoomCard({ room, onClick }) {
  return (
    <button className="room-card" type="button" onClick={() => onClick(room)}>
      <div className="top">
        <div className="tag"><span className="dt"></span>{room.live ? "Live · in your reach" : room.when}</div>
        {room.live ? <span className="pill-state">{room.present}</span> : <span className="pill-state">archive</span>}
      </div>
      <div>
        <div className="name">{room.name}</div>
        <div className="place">{room.place}</div>
      </div>
      <div className="swatch-row">
        {(room.moods || []).slice(0, 4).map((m, index) => (
          <span key={index} className="swatch" style={{ "--sw": moodColor(m) }}></span>
        ))}
        <span className="count">
          {room.live
            ? <><strong>{room.open}</strong> open · <strong>{room.present}</strong> present</>
            : <>last seen · <strong>{room.when || "—"}</strong></>}
        </span>
      </div>
    </button>
  );
}

function MoodSelector({ value, onChange }) {
  return (
    <div className="mood-card">
      <div className="left">
        <span className="eyebrow">Your signal</span>
        <h3>Set your mood — quietly.</h3>
        <p className="body">
          This is the only thing others see before mutual interest. No name, no photo — just a soft texture of what you're open to right now.
        </p>
      </div>
      <div className="mood-row">
        {MOODS.map((mood) => (
          <button
            key={mood.key}
            className={"mood-chip " + (value === mood.key ? "on" : "")}
            style={{ "--c": mood.color }}
            onClick={() => onChange(mood.key)}
          >
            <span className="swatch"></span>
            {mood.label}
          </button>
        ))}
      </div>
    </div>
  );
}

function ScanModal({ open, onClose, onScanned }) {
  const videoRef = useRef(null);
  const frameRef = useRef(0);
  const detectorRef = useRef(null);
  const streamRef = useRef(null);
  const closeTimerRef = useRef(0);
  const [found, setFound] = useState(false);
  const [blocked, setBlocked] = useState(false);
  const [blockedReason, setBlockedReason] = useState("");
  const [detectedCode, setDetectedCode] = useState("");

  const [uploadError, setUploadError] = useState(null);
  const [isProcessingUpload, setIsProcessingUpload] = useState(false);
  const fileInputRef = useRef(null);

  const isSupported = typeof BarcodeDetector !== "undefined" || typeof window.jsQR !== "undefined";

  const roomMap = useMemo(function () {
    const map = {};
    HOME_FALLBACK_NEARBY.forEach((room) => { map[room.code] = room.id; });
    return map;
  }, []);

  const handleFileUpload = async (e) => {
    const file = e.target.files && e.target.files[0];
    if (!file) return;

    setUploadError(null);
    setIsProcessingUpload(true);

    try {
      if (!file.type.startsWith("image/")) {
        setUploadError("Please select an image file.");
        return;
      }

      if (!isSupported) {
        setUploadError("QR scanning is not supported in this browser.");
        return;
      }

      let detector = detectorRef.current;
      if (!detector && typeof BarcodeDetector !== "undefined") {
        detector = new BarcodeDetector({ formats: ["qr_code"] });
        detectorRef.current = detector;
      }

      let imageBitmap;
      try {
        imageBitmap = await createImageBitmap(file);
      } catch (err) {
        setUploadError("Could not read the selected image. Please try another file.");
        return;
      }

      let hitRawValue = null;
      try {
        if (detector) {
          const detections = await detector.detect(imageBitmap);
          if (detections && detections[0]) hitRawValue = detections[0].rawValue;
        } else if (typeof window.jsQR !== "undefined") {
          const canvas = document.createElement("canvas");
          canvas.width = imageBitmap.width;
          canvas.height = imageBitmap.height;
          const ctx = canvas.getContext("2d");
          ctx.drawImage(imageBitmap, 0, 0);
          const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
          const result = window.jsQR(imageData.data, imageData.width, imageData.height, { inversionAttempts: "dontInvert" });
          if (result) hitRawValue = result.data;
        }
      } catch (err) {
        setUploadError("QR detection failed. Please try again.");
        return;
      }

      if (hitRawValue) {
        const code = String(hitRawValue).replace(/[^a-z0-9-]/gi, "").toUpperCase();
        const roomId = roomMap[code] || code.toLowerCase();
        setDetectedCode(code);
        setFound(true);
        closeTimerRef.current = window.setTimeout(() => {
          if (onScanned) onScanned(roomId, code);
        }, 1100);
      } else {
        setUploadError("No QR code found in this image.");
      }
    } catch (err) {
      setUploadError("QR detection failed. Please try again.");
    } finally {
      setIsProcessingUpload(false);
      if (e.target) {
        e.target.value = "";
      }
    }
  };

  useEffect(function () {
    if (!open) {
      setFound(false);
      setBlocked(false);
      setBlockedReason("");
      setDetectedCode("");
      setUploadError(null);
      setIsProcessingUpload(false);
      if (frameRef.current) cancelAnimationFrame(frameRef.current);
      if (closeTimerRef.current) clearTimeout(closeTimerRef.current);
      closeTimerRef.current = 0;
      if (streamRef.current && streamRef.current.getTracks) {
        streamRef.current.getTracks().forEach((track) => track.stop());
      }
      streamRef.current = null;
      return undefined;
    }

    let cancelled = false;
    const startCamera = async () => {
      try {
        const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } });
        if (cancelled) return;
        streamRef.current = stream;
        if (videoRef.current) {
          videoRef.current.srcObject = stream;
          await videoRef.current.play();
        }
      } catch (error) {
        if (cancelled) return;
        setBlocked(true);
        setBlockedReason("Camera blocked · use a code below");
      }
    };

    const detector = typeof BarcodeDetector !== "undefined" ? new BarcodeDetector({ formats: ["qr_code"] }) : null;
    detectorRef.current = detector;
    startCamera();

    const tick = async () => {
      if (!open || cancelled || closeTimerRef.current) return;
      const video = videoRef.current;
      if (!video || video.readyState < 2 || blocked || (!detectorRef.current && typeof window.jsQR === "undefined")) {
        frameRef.current = requestAnimationFrame(tick);
        return;
      }
      try {
        let hitRawValue = null;
        if (detectorRef.current) {
          const detections = await detectorRef.current.detect(video);
          if (detections && detections[0]) hitRawValue = detections[0].rawValue;
        } else if (typeof window.jsQR !== "undefined") {
          const canvas = document.createElement("canvas");
          canvas.width = video.videoWidth;
          canvas.height = video.videoHeight;
          const ctx = canvas.getContext("2d", { willReadFrequently: true });
          ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
          const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
          const result = window.jsQR(imageData.data, imageData.width, imageData.height, { inversionAttempts: "dontInvert" });
          if (result) hitRawValue = result.data;
        }

        if (hitRawValue) {
          const code = String(hitRawValue).replace(/[^a-z0-9-]/gi, "").toUpperCase();
          const roomId = roomMap[code] || code.toLowerCase();
          setDetectedCode(code);
          setFound(true);
          closeTimerRef.current = window.setTimeout(() => {
            if (onScanned) onScanned(roomId, code);
          }, 1100);
          return;
        }
      } catch (error) {
        // Ignore transient detector errors and keep scanning.
      }
      frameRef.current = requestAnimationFrame(tick);
    };

    frameRef.current = requestAnimationFrame(tick);
    return function () {
      cancelled = true;
      if (frameRef.current) cancelAnimationFrame(frameRef.current);
      if (closeTimerRef.current) clearTimeout(closeTimerRef.current);
      closeTimerRef.current = 0;
      if (streamRef.current && streamRef.current.getTracks) {
        streamRef.current.getTracks().forEach((track) => track.stop());
      }
      streamRef.current = null;
    };
  }, [blocked, onClose, onScanned, open, roomMap]);

  useEffect(function () {
    const onKey = (e) => { if (e.key === "Escape" && open) onClose(); };
    window.addEventListener("keydown", onKey);
    return function () { window.removeEventListener("keydown", onKey); };
  }, [open, onClose]);

  return (
    <div className={"modal-back " + (open ? "in" : "")} onClick={onClose}>
      <style>{`
        .upload-divider {
          display: flex;
          align-items: center;
          text-align: center;
          color: var(--mute);
          font: 500 11px var(--mono);
          letter-spacing: .08em;
          text-transform: uppercase;
          margin: 16px 0;
        }
        .upload-divider::before, .upload-divider::after {
          content: '';
          flex: 1;
          border-bottom: 1px solid var(--line);
        }
        .upload-divider::before {
          margin-right: 12px;
        }
        .upload-divider::after {
          margin-left: 12px;
        }
        .upload-area {
          display: flex;
          flex-direction: column;
          align-items: center;
          width: 100%;
        }
        .upload-btn-container {
          display: flex;
          flex-direction: column;
          align-items: center;
          width: 100%;
        }
        .upload-btn {
          appearance: none;
          display: inline-flex;
          align-items: center;
          justify-content: center;
          gap: 8px;
          min-height: 44px;
          padding: 10px 24px;
          border-radius: 999px;
          border: 1px solid var(--line);
          background: color-mix(in oklab, var(--bg) 94%, transparent);
          color: var(--ink);
          font: 500 13px/1 var(--sans);
          cursor: pointer;
          transition: transform 0.2s ease, border-color 0.2s ease, background 0.2s ease;
          width: 100%;
          max-width: 320px;
        }
        .upload-btn:hover:not(:disabled) {
          transform: translateY(-1px);
          border-color: var(--ink);
        }
        .upload-btn:disabled {
          opacity: 0.6;
          cursor: not-allowed;
        }
        .upload-error {
          color: var(--m-coral);
          font: 500 11px/1.4 var(--mono);
          letter-spacing: .06em;
          text-transform: uppercase;
          margin-top: 8px;
          text-align: center;
          max-width: 100%;
          word-break: break-word;
        }
        .upload-unsupported {
          font: 500 11px/1.4 var(--mono);
          letter-spacing: .06em;
          text-transform: uppercase;
          color: var(--mute);
          text-align: center;
          padding: 10px;
          border: 1px dashed var(--line);
          border-radius: 12px;
          width: 100%;
          max-width: 320px;
        }
      `}</style>
      <div className="modal" onClick={(e) => e.stopPropagation()}>
        <div className="head">
          <div>
            <div className="ttl">Point at the room mark</div>
            <div className="sub">Find the Herely glyph on the wall, table, or door</div>
          </div>
          <button className="modal-close" onClick={onClose} aria-label="Close"><CloseIcon /></button>
        </div>

        <div className="viewfinder">
          <video ref={videoRef} playsInline muted autoPlay className="vf-video"></video>
          <div className="vf-grid"></div>
          <div className="vf-corners">
            <span className="vf-corner tl"></span>
            <span className="vf-corner tr"></span>
            <span className="vf-corner bl"></span>
            <span className="vf-corner br"></span>
          </div>
          {!found && !blocked && <div className="vf-laser"></div>}
          {found && (
            <div className="vf-foundqr show">
              {Array.from({ length: 121 }, (_, index) => ((index + detectedCode.length) % 3 === 0 ? 1 : 0)).map((value, index) => <span key={index} className={value ? "" : "off"}></span>)}
            </div>
          )}
          <div className={"vf-status " + (found ? "found" : "") }>
            <span className="d"></span>
            {blocked ? blockedReason : found ? "Match · " + (detectedCode || "Studio 1") : "Scanning the room…"}
          </div>
          {blocked && (
            <div className="vf-fallback">
              <p>Camera blocked · use a code below</p>
              <button className="btn btn-primary" onClick={onClose}>OK</button>
            </div>
          )}
        </div>

        <div className="upload-divider">
          <span>or</span>
        </div>
        <div className="upload-area">
          <input
            ref={fileInputRef}
            type="file"
            accept="image/*"
            onChange={handleFileUpload}
            style={{ display: "none" }}
          />
          <div className="upload-btn-container">
            {!isSupported ? (
              <div className="upload-unsupported">
                QR scanning is not supported in this browser
              </div>
            ) : (
              <button
                className="upload-btn"
                type="button"
                disabled={isProcessingUpload || found}
                onClick={() => fileInputRef.current && fileInputRef.current.click()}
              >
                <QRIcon size={15} />
                {isProcessingUpload ? "Processing..." : "Upload from Device / Gallery"}
              </button>
            )}
            {uploadError && <div className="upload-error">{uploadError}</div>}
          </div>
        </div>

        <div className="modal-foot">
          <span className="hint">Auto-joins when matched</span>
          <button className="btn btn-ghost" onClick={onClose}>Cancel</button>
        </div>
      </div>
    </div>
  );
}

function Toast({ msg, show }) {
  return (
    <div className={"toast " + (show ? "in" : "") }>
      <span className="d"></span>{msg}
    </div>
  );
}

function HomeFooter() {
  return (
    <footer className="home-footer">
      <div className="wrap foot">
        <BrandLogo />
        <div className="col">
          <h5>Presence</h5>
          <a href="/home">Home</a>
          <a href="/home">Rooms</a>
        </div>
        <div className="col">
          <h5>Account</h5>
          <a href="/home">Profile</a>
          <a href="/home">Mood</a>
        </div>
        <div className="col">
          <h5>Contact</h5>
          <a href="mailto:hello@herely.ai">hello@herely.ai</a>
          <a href="/home">Support</a>
        </div>
      </div>
      <div className="wrap foot-base">
        <div>© 2026 Herely. A quieter layer of reality.</div>
        <div className="mono">Made with intention · Berlin · NYC</div>
      </div>
    </footer>
  );
}

const styles = `
    .home-shell{min-height:100vh;background:var(--bg);color:var(--ink);overflow-x:hidden}
    .home-shell .nav{position:sticky;top:0}
    .home-nav .nav-context{display:inline-flex;align-items:center;gap:8px;font:500 11px/1 var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--mute);text-decoration:none;white-space:nowrap}
    .home-nav .nav-context.is-hidden{opacity:0;pointer-events:none}
    .home-nav .nav-context.is-link:hover{color:var(--ink)}
    .home-nav .nav-context .dot{width:6px;height:6px;border-radius:50%;background:var(--m-teal);box-shadow:0 0 0 4px color-mix(in oklab, var(--m-teal) 22%, transparent)}
    .home-nav .nav-context .where{color:var(--mute)}
    .nav-right{display:flex;align-items:center;gap:10px}
    .avatar{appearance:none;border:1px solid var(--line);background:color-mix(in oklab, var(--bg) 92%, transparent);color:var(--ink);width:38px;height:38px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font:500 13px/1 var(--sans);cursor:pointer;transition:transform .2s ease,border-color .2s ease,background .2s ease}
    .avatar:hover{transform:translateY(-1px);border-color:var(--ink)}
    .home-page{padding:96px 0 40px;position:relative}
    .home-grid{display:grid;grid-template-columns:repeat(12,1fr);gap:24px}
    .greet{grid-column:1/-1;display:flex;flex-direction:column;gap:12px;padding-bottom:8px}
    .greet .row1{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap}
    .greet h1{margin:0;font-size:clamp(38px, 5.4vw, 78px);line-height:.95;letter-spacing:-.035em;font-weight:400;max-width:none;white-space:nowrap}
    .greet .lede{max-width:54ch}
    .actions{grid-column:1/-1;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:18px;margin-top:12px;margin-bottom:clamp(22px,3vw,40px)}
    .action-card{border:1px solid var(--line);border-radius:24px;background:color-mix(in oklab, var(--bg) 94%, transparent);padding:24px;min-height:280px;display:flex;flex-direction:column;gap:16px;overflow:hidden;position:relative;box-shadow:0 1px 0 rgba(255,255,255,.5) inset, 0 14px 40px rgba(11,11,13,.05)}
    .midnight .action-card{background:color-mix(in oklab, #121217 92%, transparent)}
    .action-card h2{margin:0;font-size:clamp(28px, 3vw, 44px);line-height:1.04;letter-spacing:-.03em;font-weight:400}
    .action-card .body{margin:0;color:var(--mute);font-size:15px;line-height:1.55;max-width:40ch}
    .ah{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
    .tag{display:inline-flex;align-items:center;gap:6px;font-family:var(--mono);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--mute);padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--bg)}
    .midnight .tag{background:rgba(255,255,255,.02)}
    .dt{width:6px;height:6px;border-radius:50%;background:var(--m-teal)}
    .qr-preview{width:82px;height:82px;display:grid;grid-template-columns:repeat(7,1fr);gap:3px;flex:0 0 auto;padding:4px;border-radius:18px;background:color-mix(in oklab, var(--bg) 94%, transparent);border:1px solid var(--line)}
    .midnight .qr-preview{background:rgba(255,255,255,.03)}
    .qr-preview span{border-radius:2px;background:var(--ink)}
    .qr-preview span.off{background:var(--line)}
    .foot{margin-top:auto;display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}
    .code-input{display:grid;grid-template-columns:repeat(3,minmax(0,1fr)) auto repeat(3,minmax(0,1fr));gap:10px;align-items:center}
    .code-cell{appearance:none;box-sizing:border-box;width:100%;min-width:0;aspect-ratio:1;border:1px solid var(--line);border-radius:16px;background:var(--bg);color:var(--ink);font:500 clamp(18px, 2vw, 26px)/1 var(--mono);text-align:center;outline:none;transition:border-color .15s ease,transform .15s ease,background .15s ease}
    .midnight .code-cell{background:rgba(255,255,255,.03)}
    .code-cell:focus{border-color:var(--ink);transform:translateY(-1px)}
    .code-cell.filled{border-color:color-mix(in oklab, var(--ink) 40%, var(--line))}
    .code-cell.err{border-color:var(--m-coral);animation:shake .42s ease}
    .code-sep{font-family:var(--mono);color:var(--mute);font-size:24px;line-height:1;user-select:none}
    .code-msg{font:500 11px/1 var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--mute)}
    .code-msg.ok{color:var(--m-teal)}
    .code-msg.err{color:var(--m-coral)}
    .shake{animation:shake .42s ease}
    @keyframes shake{0%,100%{transform:translateX(0)}20%{transform:translateX(-4px)}40%{transform:translateX(4px)}60%{transform:translateX(-3px)}80%{transform:translateX(3px)}}
    .now-card{grid-column:1/-1;display:grid;grid-template-columns:1.2fr .8fr;gap:24px;border:1px solid var(--line);border-radius:28px;background:var(--bg-2);padding:28px;box-shadow:0 16px 42px rgba(11,11,13,.05);margin-top:clamp(32px,5vw,64px)}
    .midnight .now-card{background:#111115}
    .crumbs{display:flex;align-items:center;gap:8px;font:500 11px/1 var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--mute);flex-wrap:wrap}
    .live{display:inline-flex;align-items:center;gap:7px}
    .live .d{width:6px;height:6px;border-radius:50%;background:var(--m-teal);box-shadow:0 0 0 4px color-mix(in oklab, var(--m-teal) 22%, transparent);animation:breathe 3.4s ease-in-out infinite}
    .title{font-size:clamp(26px, 3vw, 42px);line-height:1.05;letter-spacing:-.03em;font-weight:400;margin:8px 0 0}
    .stats{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:18px;margin-top:22px}
    .s{display:flex;flex-direction:column;gap:6px}
    .s .v{font-family:var(--serif);font-style:italic;font-size:32px;line-height:1}
    .s .l{font:500 11px/1 var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--mute)}
    .actions-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:20px}
    .now-card .right{display:flex;align-items:center;justify-content:center;min-height:280px}
    .rooms-block{grid-column:1/-1;margin-top:8px}
    .block-head{display:flex;align-items:end;justify-content:space-between;gap:16px;margin-bottom:16px;flex-wrap:wrap}
    .block-head h2{margin:0;font-size:clamp(24px, 4vw, 48px);line-height:1.02;letter-spacing:-.03em;font-weight:400}
    .block-head .right{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
    .link-mini{appearance:none;border:0;background:transparent;color:var(--ink);display:inline-flex;align-items:center;gap:8px;padding:0;font:500 13px/1 var(--sans);cursor:pointer}
    .rooms{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}
    .room-card{appearance:none;text-align:left;border:1px solid var(--line);border-radius:20px;background:var(--bg);padding:18px;display:flex;flex-direction:column;gap:16px;cursor:pointer;transition:transform .2s ease,border-color .2s ease,background .2s ease,box-shadow .2s ease;min-height:180px;box-shadow:0 1px 0 rgba(255,255,255,.5) inset}
    .midnight .room-card{background:rgba(255,255,255,.02)}
    .room-card:hover{transform:translateY(-2px);border-color:var(--ink);box-shadow:0 14px 30px rgba(11,11,13,.08)}
    .top{display:flex;align-items:center;justify-content:space-between;gap:10px}
    .pill-state{font:500 10.5px/1 var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--mute);padding:5px 8px;border-radius:999px;border:1px solid var(--line)}
    .room-card .name{font-size:22px;line-height:1.05;letter-spacing:-.02em;font-weight:400;color:var(--ink)}
    .room-card .place{margin-top:6px;color:var(--mute);font-size:13px}
    .swatch-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:auto}
    .swatch{width:10px;height:10px;border-radius:50%;background:var(--sw);box-shadow:0 0 0 4px color-mix(in oklab, var(--sw) 16%, transparent)}
    .count{font:500 11px/1.4 var(--mono);letter-spacing:.06em;text-transform:uppercase;color:var(--mute)}
    .mood-card{display:flex;flex-direction:column;align-items:flex-start;justify-content:space-between;gap:18px;padding:24px;border:1px solid var(--line);border-radius:24px;background:var(--bg-2);min-height:280px}
    .mood-card h3{margin:10px 0 0;font-size:clamp(24px, 2.8vw, 38px);line-height:1.04;letter-spacing:-.03em;font-weight:400}
    .mood-card .body{margin:10px 0 0;color:var(--mute);max-width:44ch}
    .mood-row{display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-start;margin-top:auto}
    .mood-chip{appearance:none;display:inline-flex;align-items:center;gap:8px;border-radius:999px;border:1px solid var(--line);background:transparent;color:var(--ink);padding:10px 14px;font:500 13px/1 var(--sans);cursor:pointer;transition:transform .2s ease,border-color .2s ease,background .2s ease,color .2s ease}
    .mood-chip:hover{transform:translateY(-1px);border-color:var(--ink)}
    .mood-chip.on{background:var(--ink);color:var(--bg);border-color:var(--ink)}
    .mood-chip .swatch{width:9px;height:9px;border-radius:50%;box-shadow:none;background:var(--c)}
    .modal-back{position:fixed;inset:0;z-index:9998;background:rgba(11,11,13,.58);display:flex;align-items:center;justify-content:center;padding:20px;opacity:0;pointer-events:none;transition:opacity .2s ease}
    .modal-back.in{opacity:1;pointer-events:auto}
    .modal{width:min(900px, 100%);border-radius:28px;background:var(--bg);color:var(--ink);border:1px solid var(--line);box-shadow:0 40px 120px rgba(0,0,0,.32);padding:22px;display:flex;flex-direction:column;gap:16px}
    .head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}
    .ttl{font-size:22px;line-height:1.05;letter-spacing:-.02em;font-weight:500}
    .sub{margin-top:4px;color:var(--mute);font-size:13px}
    .modal-close{appearance:none;border:1px solid var(--line);background:transparent;color:var(--ink);width:34px;height:34px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;cursor:pointer}
    .viewfinder{position:relative;min-height:420px;border-radius:24px;overflow:hidden;border:1px solid var(--line);background:#111}
    .vf-video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:.9}
    .vf-grid{position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,.05) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,.05) 1px, transparent 1px);background-size:48px 48px;mix-blend-mode:overlay;pointer-events:none}
    .vf-corners{position:absolute;inset:18px;pointer-events:none}
    .vf-corner{position:absolute;width:26px;height:26px;border-color:var(--m-teal);border-style:solid;opacity:.95}
    .vf-corner.tl{left:0;top:0;border-width:2px 0 0 2px;border-top-left-radius:18px}
    .vf-corner.tr{right:0;top:0;border-width:2px 2px 0 0;border-top-right-radius:18px}
    .vf-corner.bl{left:0;bottom:0;border-width:0 0 2px 2px;border-bottom-left-radius:18px}
    .vf-corner.br{right:0;bottom:0;border-width:0 2px 2px 0;border-bottom-right-radius:18px}
    .vf-laser{position:absolute;left:14%;right:14%;top:50%;height:2px;background:linear-gradient(90deg, transparent, var(--m-teal), transparent);box-shadow:0 0 22px var(--m-teal);animation:laser 1.8s ease-in-out infinite}
    @keyframes laser{0%,100%{transform:translateY(-20px);opacity:.45}50%{transform:translateY(20px);opacity:1}}
    .vf-foundqr{position:absolute;inset:auto 18px 18px auto;width:120px;height:120px;padding:8px;border-radius:16px;background:rgba(255,255,255,.96);display:grid;grid-template-columns:repeat(11,1fr);gap:2px;opacity:0;transform:scale(.96);transition:opacity .2s ease,transform .2s ease}
    .vf-foundqr.show{opacity:1;transform:scale(1)}
    .vf-foundqr span{background:#0B0B0D;border-radius:1px}
    .vf-foundqr span.off{background:rgba(11,11,13,.16)}
    .vf-status{position:absolute;left:16px;bottom:16px;display:inline-flex;align-items:center;gap:8px;padding:8px 12px;border-radius:999px;background:rgba(11,11,13,.62);color:#F4F2EE;font:500 11px/1 var(--mono);letter-spacing:.08em;text-transform:uppercase}
    .vf-status .d{width:7px;height:7px;border-radius:50%;background:var(--m-teal);box-shadow:0 0 0 4px rgba(0,196,154,.18)}
    .vf-status.found{background:rgba(0,196,154,.18)}
    .vf-fallback{position:absolute;inset:auto 16px 16px auto;max-width:240px;background:rgba(11,11,13,.72);color:#fff;padding:14px;border-radius:16px;display:flex;flex-direction:column;gap:10px}
    .vf-fallback p{margin:0;font-size:13px;line-height:1.5}
    .modal-foot{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}
    .hint{color:var(--mute);font:500 11px/1 var(--mono);letter-spacing:.08em;text-transform:uppercase}
    .toast{position:fixed;left:50%;bottom:28px;transform:translateX(-50%) translateY(20px);opacity:0;z-index:9999;background:rgba(11,11,13,.92);color:#F4F2EE;padding:12px 16px;border-radius:999px;display:inline-flex;align-items:center;gap:8px;font:500 13px/1 var(--sans);box-shadow:0 12px 32px rgba(0,0,0,.28);transition:opacity .2s ease,transform .2s ease}
    .toast.in{opacity:1;transform:translateX(-50%) translateY(0)}
    .toast .d{width:7px;height:7px;border-radius:50%;background:var(--m-teal);box-shadow:0 0 0 4px rgba(0,196,154,.15)}
    .home-footer{padding-top:84px}
    @media (max-width:980px){
      .actions,.now-card{grid-template-columns:1fr;display:grid}
      .actions{grid-template-columns:1fr;margin-bottom:24px}
      .mood-card{min-height:auto}
      .rooms{grid-template-columns:1fr}
      .mood-row{justify-content:flex-start}
      .now-card{margin-top:24px}
    }
    @media (max-width:720px){
      .home-page{padding-top:90px}
      .action-card{min-height:240px}
      .viewfinder{min-height:300px}
      .nav-context{display:none}
      .greet h1{white-space:normal}
      .actions{margin-bottom:18px}
      .now-card{margin-top:20px}
    }
    @media (max-width:360px){
      .code-input{gap:6px}
      .code-cell{min-width:0}
    }

    /* ── Profile Drawer ── */
    .pd-back{
      position:fixed;inset:0;z-index:110;
      background:color-mix(in oklab, #0B0B0D 38%, transparent);
      opacity:0;pointer-events:none;visibility:hidden;
      transition:opacity .35s cubic-bezier(.2,.7,.2,1), visibility 0s linear .35s;
    }
    .pd-back.in{
      opacity:1;pointer-events:auto;visibility:visible;
      backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
      transition:opacity .35s cubic-bezier(.2,.7,.2,1), visibility 0s linear 0s;
    }
    .pd{
      position:fixed;top:0;right:0;bottom:0;z-index:111;
      width:min(380px, 92vw);
      background:var(--bg);color:var(--ink);
      border-left:1px solid var(--line);
      box-shadow:-40px 0 80px rgba(11,11,13,.18);
      transform:translateX(100%);
      transition:transform .42s cubic-bezier(.22,.8,.24,1);
      display:flex;flex-direction:column;overflow:hidden;
    }
    .midnight .pd{box-shadow:-40px 0 80px rgba(0,0,0,.55)}
    .pd.in{transform:translateX(0)}
    .pd-head{
      position:relative;padding:18px 20px 14px;
      display:flex;align-items:center;justify-content:space-between;
      border-bottom:1px solid var(--line-2);
    }
    .pd-head .eyebrow{font-size:10.5px}
    .pd-close{
      appearance:none;background:transparent;color:var(--ink);
      border:1px solid var(--line);width:30px;height:30px;border-radius:999px;
      display:inline-flex;align-items:center;justify-content:center;cursor:pointer;
      transition:background .2s, border-color .2s;
    }
    .pd-close:hover{background:rgba(11,11,13,.04);border-color:var(--ink)}
    .midnight .pd-close:hover{background:rgba(255,255,255,.06)}
    .pd-body{flex:1;overflow-y:auto;padding:22px 22px 28px;display:flex;flex-direction:column;gap:22px}
    .pd-body::-webkit-scrollbar{width:0}

    /* Hero — identity card */
    .pd-id{
      display:flex;flex-direction:column;gap:14px;
      padding-bottom:22px;border-bottom:1px solid var(--line-2);
    }
    .pd-id .row{display:flex;align-items:center;gap:14px}
    .pd-av{
      position:relative;width:64px;height:64px;border-radius:50%;
      border:1px solid var(--line);
      background:color-mix(in oklab, var(--m-blue) 10%, var(--bg));
      display:flex;align-items:center;justify-content:center;
      font-family:var(--serif);font-style:italic;font-size:30px;color:var(--ink);
      flex-shrink:0;
    }
    .pd-av .ring{
      position:absolute;inset:-5px;border-radius:50%;
      border:1.5px solid var(--mc, var(--m-blue));
      opacity:.55;
    }
    .pd-av .live-dot{
      position:absolute;right:0;bottom:2px;width:14px;height:14px;border-radius:50%;
      background:var(--mc, var(--m-blue));
      box-shadow:0 0 0 3px var(--bg), 0 0 0 4px color-mix(in oklab, var(--mc, var(--m-blue)) 30%, transparent);
    }
    .pd-id .name{font-size:24px;font-weight:400;letter-spacing:-0.018em;line-height:1.1}
    .pd-id .name .ed{font-style:italic;font-family:var(--serif)}
    .pd-id .handle{font-family:var(--mono);font-size:11px;letter-spacing:.10em;text-transform:uppercase;color:var(--mute);margin-top:4px}

    .pd-mood{
      display:flex;align-items:center;gap:10px;padding:10px 14px;border-radius:14px;
      border:1px solid var(--line);background:var(--bg-2);
    }
    .pd-mood .sw{width:9px;height:9px;border-radius:50%;background:var(--mc);box-shadow:0 0 0 4px color-mix(in oklab, var(--mc) 22%, transparent)}
    .pd-mood .txt{display:flex;flex-direction:column;gap:1px;min-width:0;flex:1}
    .pd-mood .l1{font-size:13px;font-weight:500;letter-spacing:-0.005em;color:var(--ink)}
    .pd-mood .l2{font-size:11.5px;color:var(--mute)}
    .pd-mood .edit{
      font-family:var(--mono);font-size:9.5px;letter-spacing:.10em;text-transform:uppercase;color:var(--mute);
      background:transparent;border:0;cursor:pointer;padding:4px 0;
    }
    .pd-mood .edit:hover{color:var(--ink)}

    /* Section */
    .pd-sec{display:flex;flex-direction:column;gap:12px}
    .pd-sec .head{
      display:flex;align-items:center;justify-content:space-between;gap:8px;
    }
    .pd-sec .head .lab{font-family:var(--mono);font-size:10px;letter-spacing:.14em;color:var(--mute);text-transform:uppercase}
    .pd-sec .head .aux{font-family:var(--mono);font-size:10px;letter-spacing:.10em;color:var(--mute-2);text-transform:uppercase}

    /* Stats */
    .pd-stats{
      display:grid;grid-template-columns:repeat(3,1fr);
      border:1px solid var(--line);border-radius:14px;background:var(--bg);overflow:hidden;
    }
    .pd-stats .cell{padding:14px 12px;display:flex;flex-direction:column;gap:4px;border-right:1px solid var(--line-2)}
    .pd-stats .cell:last-child{border-right:0}
    .pd-stats .v{font-family:var(--serif);font-style:italic;font-size:24px;line-height:1;letter-spacing:-0.01em;color:var(--ink)}
    .pd-stats .l{font-family:var(--mono);font-size:9.5px;letter-spacing:.10em;color:var(--mute);text-transform:uppercase;margin-top:6px}

    /* Where (currently in) */
    .pd-where{
      display:flex;align-items:center;gap:12px;padding:12px 14px;
      border:1px solid var(--line);border-radius:14px;background:var(--bg);
    }
    .pd-where .d{width:7px;height:7px;border-radius:50%;background:var(--m-teal);box-shadow:0 0 0 4px color-mix(in oklab,var(--m-teal) 22%, transparent);animation:breathe 3.4s ease-in-out infinite;flex-shrink:0}
    .pd-where .col{display:flex;flex-direction:column;gap:1px;min-width:0;flex:1}
    .pd-where .l1{font-size:13px;font-weight:500;letter-spacing:-0.005em}
    .pd-where .l2{font-size:11.5px;color:var(--mute)}
    .pd-where .lk{font-family:var(--mono);font-size:9.5px;letter-spacing:.10em;color:var(--mute);text-transform:uppercase;background:none;border:0;cursor:pointer}
    .pd-where .lk:hover{color:var(--ink)}

    /* Row toggles */
    .pd-toggle{
      display:flex;align-items:center;justify-content:space-between;gap:14px;
      padding:14px 0;border-bottom:1px solid var(--line-2);
    }
    .pd-toggle:last-child{border-bottom:0}
    .pd-toggle .col{display:flex;flex-direction:column;gap:3px;min-width:0;flex:1}
    .pd-toggle .l1{font-size:13.5px;font-weight:500;letter-spacing:-0.005em;color:var(--ink)}
    .pd-toggle .l2{font-size:11.5px;color:var(--mute);max-width:32ch}
    .pd-switch{
      width:38px;height:22px;border-radius:999px;background:var(--bg-2);
      border:1px solid var(--line);position:relative;cursor:pointer;flex-shrink:0;
      transition:background .25s ease, border-color .25s ease;
    }
    .pd-switch::after{
      content:"";position:absolute;left:2px;top:1.5px;width:16px;height:16px;border-radius:50%;
      background:var(--mute);transition:transform .25s cubic-bezier(.2,.7,.2,1), background .25s;
    }
    .pd-switch.on{background:var(--ink);border-color:var(--ink)}
    .midnight .pd-switch.on{background:var(--ink);border-color:var(--ink)}
    .pd-switch.on::after{transform:translateX(15px);background:var(--bg)}

    /* Quick links */
    .pd-links{display:flex;flex-direction:column}
    .pd-link{
      display:flex;align-items:center;justify-content:space-between;gap:12px;
      padding:13px 2px;border-bottom:1px solid var(--line-2);
      background:none;border-left:0;border-right:0;border-top:0;
      cursor:pointer;color:var(--ink);text-align:left;
    }
    .pd-link:last-child{border-bottom:0}
    .pd-link .left{display:flex;align-items:center;gap:12px;min-width:0;flex:1}
    .pd-link .ic{
      width:30px;height:30px;border-radius:9px;border:1px solid var(--line);
      display:inline-flex;align-items:center;justify-content:center;color:var(--ink);flex-shrink:0;background:var(--bg);
    }
    .pd-link .ttl{font-size:13.5px;font-weight:500;letter-spacing:-0.005em}
    .pd-link .desc{font-size:11.5px;color:var(--mute)}
    .pd-link .arr{color:var(--mute-2);transition:transform .2s ease, color .2s}
    .pd-link:hover .arr{transform:translateX(3px);color:var(--ink)}
    .pd-link.danger .ttl{color:var(--m-coral)}
    .pd-link.danger .ic{color:var(--m-coral);border-color:color-mix(in oklab, var(--m-coral) 30%, var(--line))}

    /* Footer */
    .pd-foot{
      padding:14px 22px 18px;border-top:1px solid var(--line-2);
      display:flex;align-items:center;justify-content:space-between;
      font-family:var(--mono);font-size:10px;letter-spacing:.12em;color:var(--mute-2);text-transform:uppercase;
    }

    /* NYC51 Check-in form */
    .nyc51-shell{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:48px 20px}
    .nyc51-card{width:100%;max-width:560px;background:color-mix(in oklab,#121217 96%,transparent);border:1px solid var(--line);border-radius:28px;padding:40px;display:flex;flex-direction:column;gap:28px}
    .nyc51-eyebrow{font:500 11px/1 var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--mute)}
    .nyc51-heading{font-size:clamp(26px,4vw,38px);line-height:1.05;letter-spacing:-.03em;font-weight:400;margin:0}
    .nyc51-field{display:flex;flex-direction:column;gap:8px}
    .nyc51-label{font:500 11px/1 var(--mono);letter-spacing:.10em;text-transform:uppercase;color:var(--mute)}
    .nyc51-input{appearance:none;border:1px solid var(--line);border-radius:14px;background:rgba(255,255,255,.03);color:var(--ink);font:15px/1.4 var(--sans);padding:13px 16px;outline:none;transition:border-color .15s;width:100%;box-sizing:border-box}
    .nyc51-input:focus{border-color:color-mix(in oklab,var(--ink) 55%,var(--line))}
    .nyc51-input::placeholder{color:var(--mute)}
    .nyc51-options{display:flex;flex-wrap:wrap;gap:8px;margin-top:2px}
    .nyc51-chip{cursor:pointer;user-select:none}
    .nyc51-chip input{position:absolute;opacity:0;width:0;height:0}
    .nyc51-chip-label{display:inline-flex;align-items:center;padding:9px 15px;border:1px solid var(--line);border-radius:999px;font:500 13px/1 var(--sans);color:var(--mute);cursor:pointer;transition:all .15s;background:transparent}
    .nyc51-chip input:checked~.nyc51-chip-label{border-color:var(--ink);color:var(--ink);background:rgba(255,255,255,.05)}
    .nyc51-row{display:grid;grid-template-columns:1fr 1fr;gap:14px}
    .nyc51-submit{appearance:none;border:0;background:var(--ink);color:#0B0B0D;font:500 14px/1 var(--sans);padding:16px 36px;border-radius:999px;cursor:pointer;transition:all .2s;align-self:flex-start;letter-spacing:-.01em}
    .nyc51-submit:hover{transform:translateY(-1px);background:var(--ink-2)}
    .nyc51-submit:disabled{opacity:.45;cursor:not-allowed;transform:none}
    .nyc51-error{font:500 12px/1.4 var(--mono);color:var(--m-coral);padding:10px 14px;border:1px solid color-mix(in oklab,var(--m-coral) 28%,var(--line));border-radius:10px}
    .nyc51-req{color:var(--m-coral);margin-left:2px}
  `;

function HomePage({ mode, onModeToggle, onNavigate }) {
  useReveal();

  const session = readSession() || readPendingUser() || { user: { name: "Anya Reyes", email: "anya@herely.ai" } };
  const user = session.user || { name: "Anya Reyes", email: "anya@herely.ai" };
  const [userName, setUserName] = useState(firstName(user.name));
  const [userMood, setUserMood] = useState(localStorage.getItem(HOME_MOOD_KEY) || "lit");
  const [activeRoomId, setActiveRoomId] = useState(localStorage.getItem(HOME_ACTIVE_ROOM_KEY) || HOME_FALLBACK_SUMMARY.roomId);
  const [currentRoomActive, setCurrentRoomActive] = useState(Boolean(activeRoomId));
  const [nearby, setNearby] = useState(HOME_FALLBACK_NEARBY);
  const [recent, setRecent] = useState(HOME_FALLBACK_RECENT);
  const [summary, setSummary] = useState(HOME_FALLBACK_SUMMARY);
  const [now, setNow] = useState(() => new Date());
  const [scanOpen, setScanOpen] = useState(false);
  const [toast, setToast] = useState({ show: false, msg: "" });
  const [profileOpen, setProfileOpen] = useState(false);

  const showToast = (msg) => {
    setToast({ show: true, msg });
    window.setTimeout(() => setToast((current) => ({ ...current, show: false })), 2400);
  };

  useEffect(() => {
    setUserName(firstName(user.name));
  }, [user.name]);

  useEffect(() => {
    const timer = window.setInterval(() => setNow(new Date()), 30000);
    return () => window.clearInterval(timer);
  }, []);

  useEffect(() => {
    const refreshRooms = async () => {
      const nearbyRooms = await HOME_API.getNearbyRooms();
      const recentRooms = await HOME_API.getRecentRooms();
      setNearby(nearbyRooms || HOME_FALLBACK_NEARBY);
      setRecent(recentRooms || HOME_FALLBACK_RECENT);
    };
    refreshRooms();
  }, []);

  useEffect(() => {
    const refreshSummary = async () => {
      if (!activeRoomId) return;
      const next = await HOME_API.getSummary(activeRoomId);
      setSummary(next || HOME_FALLBACK_SUMMARY);
      setCurrentRoomActive(true);
    };
    refreshSummary();
    const timer = window.setInterval(refreshSummary, 15000);
    return () => window.clearInterval(timer);
  }, [activeRoomId]);

  useEffect(() => {
    document.documentElement.classList.toggle("midnight", mode === "midnight");
  }, [mode]);

  const openRoom = (roomId) => {
    if (onNavigate) onNavigate("/room/" + roomId);
  };

  const handleJoinCode = async (code) => {
    // PILOT_MODE: skip API join, go straight to check-in
    // Original did: const result = await HOME_API.joinRoom(code);
    const roomCode = String(code).toUpperCase();
    const roomId = (HOME_FALLBACK_NEARBY.find((room) => room.code === roomCode) || {}).id || roomCode.toLowerCase();
    localStorage.setItem(HOME_ACTIVE_ROOM_KEY, roomId);
    localStorage.setItem("herely.roomNumber", roomCode);
    setActiveRoomId(roomId);
    setCurrentRoomActive(true);
    // Go to pilot check-in, which will call the pilot API and then redirect to the room
    window.location.href = `/NYC51`;
  };

  const handleJoinRoom = async (room) => {
    // PILOT_MODE: go through check-in
    const roomId = room.id;
    localStorage.setItem(HOME_ACTIVE_ROOM_KEY, roomId);
    localStorage.setItem("herely.roomNumber", String(room.code).toUpperCase());
    setActiveRoomId(roomId);
    setCurrentRoomActive(true);
    window.location.href = `/NYC51`;
  };

  const handleLeave = async () => {
    if (!activeRoomId) return;
    await HOME_API.leaveRoom(activeRoomId);
    localStorage.removeItem(HOME_ACTIVE_ROOM_KEY);
    setActiveRoomId(null);
    setCurrentRoomActive(false);
    showToast("You stepped out · the room continues without you.");
  };

  const handleShare = async () => {
    const inviteUrl = formatInvite(activeRoomId || summary.roomId || "studio4");
    try {
      await navigator.clipboard.writeText(inviteUrl);
      showToast("Invite link copied · G7-K2-9X");
    } catch (error) {
      window.prompt("Copy invite link", inviteUrl);
    }
  };

  const handleMoodChange = async (nextMood) => {
    setUserMood(nextMood);
    localStorage.setItem(HOME_MOOD_KEY, nextMood);
    try {
      await HOME_API.updateMood(nextMood);
    } catch (error) {
      // Optimistic local update stays in place.
    }
    showToast("Mood set · " + ((MOODS.find((item) => item.key === nextMood) || MOODS[0]).label || "open").toLowerCase());
  };

  

  return (
    <div className="home-shell">
      <style>{styles}</style>
      <ProfileDrawer
        open={profileOpen}
        onClose={() => setProfileOpen(false)}
        userName={userName}
        mood={userMood}
        onChangeMood={handleMoodChange}
        currentRoomActive={currentRoomActive}
        onLeave={handleLeave}
        onOpenSettings={(page) => showToast(`Opening settings: ${page}`)}
        onSignOut={() => showToast("Signing out...")}
      />
      <HomeNav
        mode={mode}
        onToggle={onModeToggle}
        userName={userName}
        activeRoomId={currentRoomActive ? activeRoomId : null}
        onOpenRoom={openRoom}
        onProfile={() => setProfileOpen(!profileOpen)}
        onNavigate={onNavigate}
      />

      <main className="home-page">
        <div className="wrap home-grid">
          <Greeting userName={userName} mood={userMood} time={now} />

          <div className="actions">
            <MoodSelector value={userMood} onChange={handleMoodChange} />
            <CodeCard onSubmit={handleJoinCode} />
            <ScanCard onOpen={() => setScanOpen(true)} />
          </div>

          <CurrentlyIn
            active={currentRoomActive}
            summary={summary}
            onLeave={handleLeave}
            onOpenRoom={openRoom}
            onShare={handleShare}
          />

          <section className="rooms-block rv d4">
            <div className="block-head">
              <h2>Nearby rooms <span className="ed" style={{ color: "var(--mute)" }}>· within reach</span></h2>
              <div className="right">
                <span className="mono">{nearby.filter((room) => room.live).length} live</span>
                <button className="link-mini" type="button">See all <Arrow /></button>
              </div>
            </div>
            <div className="rooms">
              {nearby.map((room) => <RoomCard key={room.id} room={room} onClick={handleJoinRoom} />)}
            </div>
          </section>

          <section className="rooms-block rv d5">
            <div className="block-head">
              <h2>Rooms you've been in <span className="ed" style={{ color: "var(--mute)" }}>· memory</span></h2>
              <div className="right">
                <button className="link-mini" type="button"><PlusIcon /> Host a room</button>
              </div>
            </div>
            <div className="rooms">
              {recent.map((room) => <RoomCard key={room.id} room={room} onClick={() => showToast(room.name + " is dormant · waiting for the next gathering")} />)}
            </div>
          </section>
        </div>
      </main>

      <HomeFooter />

      <ScanModal
        open={scanOpen}
        onClose={() => setScanOpen(false)}
        onScanned={(roomId, code) => {
          setScanOpen(false);
          const resolved = roomId || code.toLowerCase();
          localStorage.setItem(HOME_ACTIVE_ROOM_KEY, resolved);
          setActiveRoomId(resolved);
          setCurrentRoomActive(true);
          // PILOT_MODE: go through check-in
          window.location.href = `/NYC51`;
        }}
      />
      <Toast msg={toast.msg} show={toast.show} />
    </div>
  );
}

const NYC51_QUESTIONS = [
  {
    id: "q1",
    text: "What brought you here tonight?",
    options: ["Meet new people", "Support Fabio", "Have fun with friends", "Explore NYC community"],
  },
  {
    id: "q2",
    text: "What are you most open to tonight?",
    options: ["New friendships", "Professional connections", "Local recommendations", "Just enjoying the event"],
  },
  {
    id: "q3",
    text: "How long are you staying?",
    options: ["Until the show starts", "Through the show", "Staying afterward"],
  },
  {
    id: "q4",
    text: "What's your favourite club in New York?",
    type: "text",
    placeholder: "Type it in…",
  },
  {
    id: "q5",
    text: "Zodiac sign?",
    type: "text",
    placeholder: "e.g. Scorpio",
  },
];

function NYC51CheckIn({ roomId, mood, onComplete }) {
  const [name,    setName]    = useState("");
  const [email,   setEmail]   = useState("");
  const [answers, setAnswers] = useState({});
  const [loading, setLoading] = useState(false);
  const [error,   setError]   = useState("");

  const setAnswer = (id, val) => setAnswers(prev => ({ ...prev, [id]: val }));

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (!name.trim()) { setError("Name is required."); return; }
    setLoading(true);
    setError("");
    try {
      const res = await fetch("/api/pilot/join", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          name:     name.trim(),
          contact:  email.trim(),
          roomCode: (roomId || "herely").toUpperCase(),
          mood,
          answers,
        }),
      });
      const data = await res.json();
      if (!res.ok) throw new Error(data.message || "Server error " + res.status);
      localStorage.setItem("herely.pilot", JSON.stringify({
        sessionId: data.sessionId,
        name:      name.trim(),
        mood,
      }));
      onComplete(data.sessionId);
    } catch (err) {
      setError(err.message || "Something went wrong. Please try again.");
      setLoading(false);
    }
  };

  return (
    <div className="home-shell midnight">
      <style>{styles}</style>
      <div className="nyc51-shell">
        <form className="nyc51-card" onSubmit={handleSubmit} noValidate>
          <div>
            <div className="nyc51-eyebrow">Herely · NYC51</div>
            <h1 className="nyc51-heading" style={{ marginTop: "10px" }}>
              One quick thing<br/>before you step in.
            </h1>
          </div>

          <div className="nyc51-row">
            <div className="nyc51-field">
              <label className="nyc51-label">Name <span className="nyc51-req">*</span></label>
              <input
                className="nyc51-input"
                type="text"
                placeholder="Your name"
                value={name}
                onChange={e => setName(e.target.value)}
                autoFocus
              />
            </div>
            <div className="nyc51-field">
              <label className="nyc51-label">Email (optional)</label>
              <input
                className="nyc51-input"
                type="email"
                placeholder="you@email.com"
                value={email}
                onChange={e => setEmail(e.target.value)}
              />
            </div>
          </div>

          {NYC51_QUESTIONS.map(q => (
            <div key={q.id} className="nyc51-field">
              <label className="nyc51-label">{q.text}</label>
              {q.type === "text" ? (
                <input
                  className="nyc51-input"
                  type="text"
                  placeholder={q.placeholder || ""}
                  value={answers[q.id] || ""}
                  onChange={e => setAnswer(q.id, e.target.value)}
                />
              ) : (
                <div className="nyc51-options">
                  {q.options.map(opt => (
                    <label key={opt} className="nyc51-chip">
                      <input
                        type="radio"
                        name={q.id}
                        value={opt}
                        checked={answers[q.id] === opt}
                        onChange={() => setAnswer(q.id, opt)}
                      />
                      <span className="nyc51-chip-label">{opt}</span>
                    </label>
                  ))}
                </div>
              )}
            </div>
          ))}

          {error && <div className="nyc51-error">{error}</div>}

          <button className="nyc51-submit" type="submit" disabled={loading}>
            {loading ? "Joining…" : "Step in →"}
          </button>
        </form>
      </div>
    </div>
  );
}

function CopacabanaPage({ onNavigate }) {
  useReveal();
  const [step,        setStep]        = useState("home");
  const [pendingRoom, setPendingRoom] = useState("herely");
  const [scanOpen,    setScanOpen]    = useState(false);
  const [userMood,    setUserMood]    = useState(localStorage.getItem(HOME_MOOD_KEY) || "lit");

  const handleMoodChange = (mood) => {
    setUserMood(mood);
    localStorage.setItem(HOME_MOOD_KEY, mood);
  };

  const goToCheckIn = (roomId) => {
    setPendingRoom(String(roomId).toLowerCase());
    setStep("checkin");
  };

  if (step === "checkin") {
    return (
      <NYC51CheckIn
        roomId={pendingRoom}
        mood={userMood}
        onComplete={() => onNavigate("/room/" + pendingRoom)}
      />
    );
  }

  return (
    <div className="home-shell midnight">
      <style>{styles}</style>
      <main className="home-page" style={{ display: "flex", alignItems: "center", justifyContent: "center", minHeight: "100vh" }}>
        <div className="wrap" style={{ width: "100%", maxWidth: "1080px" }}>
          <div className="actions">
            <MoodSelector value={userMood} onChange={handleMoodChange} />
            <CodeCard onSubmit={goToCheckIn} />
            <ScanCard onOpen={() => setScanOpen(true)} />
          </div>
        </div>
      </main>
      <ScanModal
        open={scanOpen}
        onClose={() => setScanOpen(false)}
        onScanned={(roomId, code) => {
          setScanOpen(false);
          goToCheckIn(roomId || code.toLowerCase());
        }}
      />
    </div>
  );
}

Object.assign(window, { HomePage, CopacabanaPage, NYC51CheckIn, ScanModal, HomeNav, HomeStyles: styles });
