// components.jsx — shared reusables for the HOTMESS brand site
// Wordmark / Reveal / SpotsCounter / MemberForm / PulseGlobe / SafetyDemo / Placeholder / TierDrawer

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

// ─────────────────────────────────────────────────────────────────────────────
// useReveal — IntersectionObserver hook; adds .in class when an element enters
function useReveal(opts = {}) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting) {
            el.classList.add("in");
            io.unobserve(el);
          }
        });
      },
      { threshold: opts.threshold ?? 0.15, rootMargin: opts.rootMargin ?? "0px 0px -10% 0px" }
    );
    io.observe(el);
    return () => io.disconnect();
  }, []);
  return ref;
}

// Reveal — drop-in wrapper that adds .rv class + observes
function Reveal({ as: Tag = "div", delay = 0, className = "", children, ...rest }) {
  const ref = useReveal();
  const dCls = delay > 0 ? ` d${delay}` : "";
  return (
    <Tag ref={ref} className={`rv${dCls} ${className}`} {...rest}>
      {children}
    </Tag>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Wordmark — HOT (white) MESS (gold). Optional animated stagger.
function Wordmark({ size = "inherit", animated = false, splitGold = true, as: Tag = "span" }) {
  const letters = "HOTMESS".split("");
  if (!animated) {
    return (
      <Tag className="wm" style={{ fontSize: size }}>
        <span>HOT</span>
        <span className={splitGold ? "gold" : ""}>MESS</span>
      </Tag>
    );
  }
  return (
    <Tag className="hero-mark" aria-label="HOTMESS">
      {letters.map((ch, i) => (
        <span className="l" key={i}>
          <span style={{ color: splitGold && i >= 3 ? "var(--gold)" : "var(--ink)" }}>{ch}</span>
        </span>
      ))}
    </Tag>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Placeholder — striped block with monospace caption (for missing imagery)
function Placeholder({ label = "image", ratio = "4/3", style }) {
  return (
    <div className="ph" style={{ aspectRatio: ratio, ...style }}>
      <span className="ph-label">{label}</span>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// SpotsCounter — ticks down to a target. Used in the founding tier cards.
function SpotsCounter({ total, taken = 0, label = "spots" }) {
  const ref = useReveal();
  const [n, setN] = useState(total);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver(
      (es) => {
        es.forEach((e) => {
          if (!e.isIntersecting) return;
          const target = Math.max(0, total - taken);
          let i = total;
          const tick = () => {
            i -= 1;
            setN(i);
            if (i > target) setTimeout(tick, 60 + Math.random() * 40);
          };
          setTimeout(tick, 120);
          io.unobserve(el);
        });
      },
      { threshold: 0.4 }
    );
    io.observe(el);
    return () => io.disconnect();
  }, [total, taken]);
  return (
    <div ref={ref} style={{ display: "inline-flex", alignItems: "baseline", gap: 8 }}>
      <span className="display" style={{ fontSize: "44px", color: "var(--gold)", lineHeight: 1 }}>
        {String(n).padStart(2, "0")}
      </span>
      <span className="mono" style={{ color: "var(--ink-3)" }}>
        / {total} {label}
      </span>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// MemberForm — validates email; "submits" by holding state.
function MemberForm() {
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const [username, setUsername] = useState("");
  const [city, setCity] = useState("London");
  const [age, setAge] = useState(false);
  const [state, setState] = useState("idle"); // idle | sending | err | ok
  const [msg, setMsg] = useState("");

  // Auto-derive a sensible username from the email local-part when the
  // user hasn't typed one. Keeps it lowercase, only [a-z0-9_], capped at 20.
  const deriveUsername = (em) => {
    const local = (em || "").split("@")[0] || "";
    return local.toLowerCase().replace(/[^a-z0-9_]/g, "_").slice(0, 20);
  };

  const onEmailChange = (e) => {
    const v = e.target.value;
    setEmail(v);
    if (state !== "idle" && state !== "sending") setState("idle");
    if (!username) setUsername(deriveUsername(v));
  };

  const onSubmit = async (e) => {
    e.preventDefault();
    // Client-side guardrails (match server contract)
    if (!name.trim()) { setState("err"); setMsg("Name required."); return; }
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.trim())) {
      setState("err"); setMsg("That doesn't read like an email."); return;
    }
    if (!/^[a-z0-9_]{3,20}$/.test(username)) {
      setState("err"); setMsg("Username: 3–20 lowercase letters, numbers, or underscore."); return;
    }
    if (!age) {
      setState("err"); setMsg("Confirm you're 18+ and accept the terms.");
      return;
    }

    setState("sending"); setMsg("");
    try {
      const res = await fetch("/api/members/signup", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          full_name: name.trim(),
          email: email.trim().toLowerCase(),
          claimed_username: username,
          instagram_handle: null,
          city,
          referrer: "homepage",
          age_confirmed: true,
        }),
      });
      const data = await res.json().catch(() => ({}));
      if (!res.ok) {
        setState("err");
        setMsg(data.error || "Something went wrong. Try again.");
        return;
      }
      setState("ok");
      setMsg("In. Welcome email from welcome@hotmessldn.com is on the way — check spam.");
      setName(""); setEmail(""); setUsername(""); setAge(false);
    } catch (err) {
      setState("err");
      setMsg("Network error. Try again.");
    }
  };

  const sending = state === "sending";

  return (
    <form onSubmit={onSubmit} noValidate>
      <div className="form-row" style={{ marginBottom: 10 }}>
        <input
          type="text"
          placeholder="Your name"
          value={name}
          onChange={(e) => setName(e.target.value)}
          disabled={sending}
          aria-label="Full name"
        />
      </div>
      <div className="form-row" style={{ marginBottom: 10 }}>
        <input
          type="text"
          inputMode="email"
          autoComplete="email"
          autoCorrect="off"
          autoCapitalize="none"
          spellCheck={false}
          placeholder="you@somewhere.com"
          value={email}
          onChange={onEmailChange}
          className={state === "err" ? "err" : ""}
          disabled={sending}
          aria-label="Email address"
        />
      </div>
      <div className="form-row" style={{ marginBottom: 10 }}>
        <input
          type="text"
          autoCapitalize="none"
          autoCorrect="off"
          spellCheck={false}
          autoComplete="username"
          placeholder="username"
          value={username}
          onChange={(e) => setUsername(e.target.value.replace(/^@/, "").toLowerCase().replace(/[^a-z0-9_]/g, "").slice(0, 20))}
          disabled={sending}
          aria-label="Claimed username"
          style={{ flex: 0.6 }}
        />
        <select
          value={city}
          onChange={(e) => setCity(e.target.value)}
          disabled={sending}
          aria-label="City"
          style={{
            flex: 0.4, background: "transparent", border: "1px solid var(--line-2)",
            color: "var(--ink)", padding: "14px 16px", fontFamily: "var(--mono)",
            letterSpacing: "0.06em", fontSize: 12, outline: "none",
          }}
        >
          <option value="London">London</option>
          <option value="Manchester">Manchester</option>
          <option value="Brighton">Brighton</option>
          <option value="Other">Other</option>
        </select>
      </div>
      <label style={{
        display: "flex", gap: 10, alignItems: "flex-start",
        fontFamily: "var(--mono)", fontSize: 11, letterSpacing: "0.06em",
        color: "var(--ink-3)", marginBottom: 12, maxWidth: 520,
      }}>
        <input
          type="checkbox"
          checked={age}
          onChange={(e) => setAge(e.target.checked)}
          disabled={sending}
          style={{ marginTop: 2 }}
          aria-label="Confirm 18+ and accept terms"
        />
        <span>I'm 18 or older and accept the founding-member terms.</span>
      </label>
      <div className="form-row">
        <button type="submit" className="btn btn-solid" disabled={sending}>
          {sending ? "Sending…" : "Claim"}
        </button>
      </div>
      {msg ? (
        <div className={`form-msg ${state === "ok" ? "ok" : state === "err" ? "err" : ""}`}>{msg}</div>
      ) : (
        <div className="form-msg" style={{ color: "var(--ink-4)" }}>
          50 Original · 100 Founding · 100 Early — 250 total · free for life
        </div>
      )}
    </form>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// PulseGlobe — SVG sphere with a slow rotation, gridlines, pulsing beacons.
// Lightweight: no canvas, no WebGL. Beacons are positioned at lat/lng then
// projected onto a circle (orthographic).
const BEACONS = [
  { lng: -0.13, lat: 51.51, label: "LONDON" },     // home
  { lng: 2.35,  lat: 48.86, label: "PARIS" },
  { lng: 13.40, lat: 52.52, label: "BERLIN" },
  { lng: 2.17,  lat: 41.39, label: "BARCELONA" },
  { lng: 1.43,  lat: 38.91, label: "IBIZA" },
  { lng: -73.98, lat: 40.76, label: "NYC" },
  { lng: -118.24, lat: 34.05, label: "LA" },
  { lng: 151.21, lat: -33.86, label: "SYDNEY" },
  { lng: 139.69, lat: 35.69, label: "TOKYO" },
  { lng: -43.20, lat: -22.91, label: "RIO" },
  { lng: 28.05, lat: -26.20, label: "JOBURG" },
];

function project(lng, lat, rotation, R = 100) {
  const lambda = ((lng + rotation) * Math.PI) / 180;
  const phi = (lat * Math.PI) / 180;
  const x = R * Math.cos(phi) * Math.sin(lambda);
  const y = -R * Math.sin(phi);
  const z = Math.cos(phi) * Math.cos(lambda); // visibility
  return { x, y, z };
}

function PulseGlobe({ size = 520 }) {
  const [rot, setRot] = useState(20);
  const raf = useRef(0);
  const last = useRef(performance.now());
  useEffect(() => {
    let alive = true;
    const tick = (t) => {
      const dt = (t - last.current) / 1000;
      last.current = t;
      setRot((r) => (r + dt * 4) % 360); // 4 deg/sec
      if (alive) raf.current = requestAnimationFrame(tick);
    };
    raf.current = requestAnimationFrame(tick);
    return () => { alive = false; cancelAnimationFrame(raf.current); };
  }, []);
  const R = 100;
  const grid = useMemo(() => {
    const parallels = [-60, -30, 0, 30, 60].map((lat) => {
      const phi = (lat * Math.PI) / 180;
      const r = R * Math.cos(phi);
      const y = -R * Math.sin(phi);
      // Squish into ellipse for orthographic projection of sphere
      return { rx: r, ry: r * 0.30, cy: y };
    });
    const meridians = [];
    for (let lngBase = 0; lngBase < 180; lngBase += 30) meridians.push(lngBase);
    return { parallels, meridians };
  }, []);
  return (
    <div style={{ width: "100%", display: "flex", justifyContent: "center" }}>
      <svg viewBox="-130 -130 260 260" width={size} height={size} style={{ maxWidth: "100%", filter: "drop-shadow(0 30px 60px rgba(200,150,44,0.18))" }}>
        <defs>
          <radialGradient id="globe-shade" cx="35%" cy="35%" r="75%">
            <stop offset="0%" stopColor="#0e1419" />
            <stop offset="55%" stopColor="#070a0d" />
            <stop offset="100%" stopColor="#020203" />
          </radialGradient>
          <radialGradient id="halo" cx="50%" cy="50%" r="50%">
            <stop offset="60%" stopColor="rgba(200,150,44,0)" />
            <stop offset="100%" stopColor="rgba(200,150,44,0.22)" />
          </radialGradient>
          <filter id="beacon-glow" x="-200%" y="-200%" width="500%" height="500%">
            <feGaussianBlur stdDeviation="2" />
          </filter>
        </defs>

        {/* halo */}
        <circle cx="0" cy="0" r="120" fill="url(#halo)" />
        {/* sphere */}
        <circle cx="0" cy="0" r={R} fill="url(#globe-shade)" stroke="rgba(255,255,255,0.10)" strokeWidth="0.5" />

        {/* parallels */}
        {grid.parallels.map((p, i) => (
          <ellipse key={`par-${i}`} cx="0" cy={p.cy} rx={p.rx} ry={p.ry} fill="none" stroke="rgba(255,255,255,0.08)" strokeWidth="0.4" />
        ))}

        {/* meridians — drawn as rotated ellipses */}
        {grid.meridians.map((m, i) => {
          const a = ((m + rot) * Math.PI) / 180;
          const rx = Math.abs(R * Math.cos(a));
          return (
            <ellipse key={`mer-${i}`} cx="0" cy="0" rx={rx} ry={R} fill="none" stroke="rgba(255,255,255,0.07)" strokeWidth="0.4" />
          );
        })}

        {/* clip beacons to globe */}
        <clipPath id="globe-clip"><circle cx="0" cy="0" r={R - 1} /></clipPath>
        <g clipPath="url(#globe-clip)">
          {BEACONS.map((b, i) => {
            const p = project(b.lng, b.lat, rot, R);
            if (p.z < 0) return null;
            const opacity = 0.55 + 0.45 * p.z;
            return (
              <g key={b.label} transform={`translate(${p.x}, ${p.y})`} opacity={opacity}>
                <circle r="6" fill="var(--gold)" opacity="0.18">
                  <animate attributeName="r" values="2;9;2" dur={`${2 + (i % 3) * 0.6}s`} repeatCount="indefinite" />
                  <animate attributeName="opacity" values="0.35;0;0.35" dur={`${2 + (i % 3) * 0.6}s`} repeatCount="indefinite" />
                </circle>
                <circle r="1.6" fill="var(--gold)" filter="url(#beacon-glow)" />
                <circle r="0.9" fill="#fff" />
              </g>
            );
          })}
        </g>

        {/* terminator (day/night softening) */}
        <circle cx="0" cy="0" r={R} fill="rgba(0,0,0,0.18)" pointerEvents="none" />
      </svg>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// SafetyDemo — faithful recreation of the HOTMESS app's Safety Hub screen.
// Matches the visual language of the real Pulse screenshot: HOTMESS wordmark
// at top, big bold uppercase title, dark rounded menu panel with cyan icons,
// gold square action button, bottom tab bar.

function PhoneIcon()    { return <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#4dc0ff" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.96.37 1.9.72 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.91.35 1.85.59 2.81.72A2 2 0 0 1 22 16.92z"/></svg>; }
function ClockIcon()    { return <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#4dc0ff" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>; }
function ShieldIcon(props){ return <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke={props.color || "rgba(255,255,255,0.45)"} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>; }
function BellIcon(p)    { const c = p?.color || "rgba(255,255,255,0.85)"; return <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>; }
function BellMuted()    { return <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/><line x1="2" y1="2" x2="22" y2="22" stroke="#fff"/></svg>; }
function PinIcon()      { return <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#050507" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>; }

// iOS-style status bar icons
function SignalDots() {
  return (
    <svg width="17" height="11" viewBox="0 0 17 11" fill="#fff">
      <rect x="0"  y="7" width="2.5" height="4" rx="0.5"/>
      <rect x="4"  y="5" width="2.5" height="6" rx="0.5"/>
      <rect x="8"  y="3" width="2.5" height="8" rx="0.5"/>
      <rect x="12" y="0" width="2.5" height="11" rx="0.5"/>
    </svg>
  );
}
function WifiIcon() {
  return (
    <svg width="15" height="11" viewBox="0 0 15 11" fill="none" stroke="#fff" strokeWidth="1.4" strokeLinecap="round">
      <path d="M1 4 A 8 8 0 0 1 14 4"/>
      <path d="M3.5 6.2 A 5 5 0 0 1 11.5 6.2"/>
      <path d="M6 8.4 A 2 2 0 0 1 9 8.4"/>
      <circle cx="7.5" cy="9.6" r="0.9" fill="#fff" stroke="none"/>
    </svg>
  );
}
function BatteryIcon({ pct = 30, lowColor = "#f2c24a" }) {
  const fill = pct <= 30 ? lowColor : "#fff";
  return (
    <svg width="25" height="12" viewBox="0 0 25 12" fill="none">
      <rect x="0.5" y="0.5" width="22" height="11" rx="3" stroke="#fff" strokeOpacity="0.7"/>
      <rect x="23" y="3.5" width="1.5" height="5" rx="0.5" fill="#fff" fillOpacity="0.7"/>
      <rect x="2" y="2" width={(pct / 100) * 19} height="8" rx="1.5" fill={fill}/>
    </svg>
  );
}

// Tab-bar icons — match the actual HOTMESS app
function TabHome(p)    { return <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke={p.color} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M3 11l9-8 9 8"/><path d="M5 10v10h14V10"/></svg>; }
function TabPulse(p)   { return <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke={p.color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="3 12 6 12 9 4 12 20 15 9 18 15 21 12"/></svg>; }
function TabGhost(p)   { return <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke={p.color} strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M5 11a7 7 0 0 1 14 0v9l-2.5-2-2 2-2-2-2 2-2.5-2L5 20z"/><circle cx="9.5" cy="11" r="0.8" fill={p.color}/><circle cx="14.5" cy="11" r="0.8" fill={p.color}/></svg>; }
function TabMusic(p)   { return <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke={p.color} strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M9 18V5l11-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="17" cy="16" r="3"/></svg>; }
function TabShop(p)    { return <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke={p.color} strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M6 7h12l-1 13H7L6 7z"/><path d="M9 7V5a3 3 0 0 1 6 0v2"/></svg>; }
function TabMore(p)    { return <svg width="20" height="20" viewBox="0 0 24 24" fill={p.color}><circle cx="6" cy="6" r="1.6"/><circle cx="12" cy="6" r="1.6"/><circle cx="18" cy="6" r="1.6"/><circle cx="6" cy="12" r="1.6"/><circle cx="12" cy="12" r="1.6"/><circle cx="18" cy="12" r="1.6"/><circle cx="6" cy="18" r="1.6"/><circle cx="12" cy="18" r="1.6"/><circle cx="18" cy="18" r="1.6"/></svg>; }

function SafetyDemo() {
  const [mode, setMode] = useState("idle"); // idle | hold | sent | ringing
  const holdT = useRef(null);
  const onDown = () => {
    setMode("hold");
    holdT.current = setTimeout(() => setMode("sent"), 1400);
  };
  const onUp = () => {
    if (mode === "hold") { clearTimeout(holdT.current); setMode("idle"); }
  };
  const onFake = () => {
    setMode("ringing");
    setTimeout(() => setMode("idle"), 4600);
  };

  // App-style phone shell
  const phoneStyle = {
    position: "relative", width: "100%", maxWidth: 360,
    aspectRatio: "360/740", borderRadius: 38,
    background: "#000",
    border: "1px solid rgba(255,255,255,0.10)",
    overflow: "hidden",
    boxShadow: "0 40px 100px rgba(0,0,0,0.65), 0 0 0 6px #07070a, 0 0 0 7px #1a1a1f",
    fontFamily: "var(--display)",
  };

  return (
    <div style={phoneStyle} aria-label="HOTMESS Safety Hub demo">
      {/* Dynamic island / notch */}
      <div style={{ position: "absolute", top: 12, left: "50%", transform: "translateX(-50%)", width: 108, height: 26, background: "#000", borderRadius: 16, zIndex: 5 }}></div>

      {/* Status bar */}
      <div style={{
        position: "absolute", top: 0, left: 0, right: 0, padding: "14px 24px 0",
        display: "flex", justifyContent: "space-between", alignItems: "center",
        color: "#fff", fontFamily: "var(--display)", fontSize: 15, letterSpacing: "0",
        zIndex: 3,
      }}>
        <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
          20:34 <BellMuted />
        </span>
        <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
          <SignalDots /><WifiIcon /><BatteryIcon pct={30} />
        </span>
      </div>

      {/* HOTMESS app nav */}
      <div style={{
        position: "absolute", top: 44, left: 0, right: 0, padding: "14px 22px",
        display: "flex", justifyContent: "space-between", alignItems: "center", zIndex: 3,
      }}>
        <span style={{ fontFamily: "var(--display)", fontSize: 22, letterSpacing: "-0.01em", lineHeight: 1 }}>
          <span style={{ color: "#fff" }}>HOT</span><span style={{ color: "var(--gold)" }}>MESS</span>
        </span>
        <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
          <BellIcon />
          <div style={{ width: 26, height: 26, borderRadius: "50%", overflow: "hidden", border: "1px solid rgba(255,255,255,0.2)", backgroundImage: "url('assets/founder-1.jpg')", backgroundSize: "cover", backgroundPosition: "center" }}></div>
        </div>
      </div>

      {/* Title block */}
      {mode !== "ringing" && (
        <div style={{ position: "absolute", top: 102, left: 0, right: 0, textAlign: "center", padding: "0 24px", zIndex: 2 }}>
          <div style={{ fontFamily: "var(--display)", fontSize: 40, lineHeight: 1, color: "#fff", letterSpacing: "-0.01em" }}>SAFETY</div>
          <div style={{ fontFamily: "var(--display)", fontSize: 11, marginTop: 8, letterSpacing: "0.22em", color: "rgba(255,255,255,0.55)" }}>HOLD&nbsp;TO&nbsp;ARM&nbsp;SOS.</div>
        </div>
      )}

      {/* Armed pill */}
      {mode !== "ringing" && (
        <div style={{ position: "absolute", top: 186, left: 22, zIndex: 2, padding: "6px 12px", background: "rgba(20,20,24,0.75)", border: "1px solid rgba(255,255,255,0.10)", borderRadius: 999, display: "flex", alignItems: "center", gap: 8 }}>
          <span style={{ width: 8, height: 8, borderRadius: "50%", background: mode === "hold" ? "var(--gold)" : "#3ee07a", boxShadow: mode === "hold" ? "0 0 8px var(--gold)" : "0 0 8px #3ee07a", animation: "blink 1.6s ease-in-out infinite" }}></span>
          <span className="mono" style={{ color: "#fff", fontSize: 10 }}>
            {mode === "idle"  && "READY"}
            {mode === "hold"  && "ARMING…"}
            {mode === "sent"  && "SOS SENT"}
          </span>
        </div>
      )}

      {/* Big SOS hold button */}
      {mode !== "ringing" && (
        <div style={{ position: "absolute", top: 240, left: 0, right: 0, display: "flex", justifyContent: "center", zIndex: 2 }}>
          <button
            onMouseDown={onDown} onMouseUp={onUp} onMouseLeave={onUp}
            onTouchStart={(e) => { e.preventDefault(); onDown(); }} onTouchEnd={onUp}
            style={{
              width: 168, height: 168, borderRadius: "50%", border: "none",
              background: mode === "sent" ? "#3ee07a" : "var(--gold)",
              color: "var(--bg)", fontFamily: "var(--display)", fontSize: 30, letterSpacing: "0.02em",
              position: "relative", cursor: "pointer",
              boxShadow: mode === "hold"
                ? "0 0 0 14px rgba(200,150,44,0.20), 0 0 0 30px rgba(200,150,44,0.10), 0 0 36px rgba(200,150,44,0.4)"
                : "0 0 30px rgba(200,150,44,0.25)",
              transition: "box-shadow .25s, background .25s",
              lineHeight: 0.95,
            }}
            aria-label="Hold to arm SOS"
          >
            {mode === "sent" ? "SENT" : <>HOLD<br/>FOR<br/>SOS</>}
            {mode === "hold" && (
              <span style={{
                position: "absolute", inset: -8, borderRadius: "50%",
                border: "2px solid var(--gold)",
                animation: "sos-ring 1.4s linear",
              }}></span>
            )}
          </button>
          <style>{`@keyframes sos-ring { from{ transform: scale(0.92); opacity:1 } to{ transform: scale(1.55); opacity:0 } }`}</style>
        </div>
      )}

      {/* Safety menu panel (mirrors the in-app overlay) */}
      {mode !== "ringing" && (
        <div style={{
          position: "absolute", bottom: 96, left: 16, right: 84, zIndex: 2,
          background: "rgba(20,20,24,0.78)", backdropFilter: "blur(12px)",
          border: "1px solid rgba(255,255,255,0.10)", borderRadius: 14,
          padding: 4,
        }}>
          {[
            { icon: <PhoneIcon />,  label: "Fake Call",       onClick: onFake, active: true },
            { icon: <ClockIcon />,  label: "Check-in Timer",  onClick: () => {}, active: true },
            { icon: <ShieldIcon color="rgba(255,255,255,0.55)" />, label: "Trusted Contacts", onClick: () => {}, active: false },
          ].map((row, i) => (
            <button key={i} onClick={row.onClick} style={{
              display: "flex", alignItems: "center", gap: 12,
              width: "100%", padding: "10px 12px", background: "transparent", border: "none",
              cursor: row.active ? "pointer" : "default",
              borderTop: i > 0 ? "1px solid rgba(255,255,255,0.06)" : "none",
            }}>
              <span style={{ width: 22, display: "flex", alignItems: "center", justifyContent: "center" }}>{row.icon}</span>
              <span style={{ color: row.active ? "#fff" : "rgba(255,255,255,0.5)", fontFamily: "var(--sans)", fontSize: 14, letterSpacing: 0 }}>{row.label}</span>
            </button>
          ))}
        </div>
      )}

      {/* Gold pin button bottom-right (drops a beacon) */}
      {mode !== "ringing" && (
        <button aria-label="Drop beacon" style={{
          position: "absolute", bottom: 102, right: 16, zIndex: 2,
          width: 52, height: 52, borderRadius: 12, border: "none",
          background: "var(--gold)",
          boxShadow: "0 0 24px rgba(200,150,44,0.35)",
          display: "flex", alignItems: "center", justifyContent: "center", cursor: "pointer",
        }}>
          <PinIcon />
        </button>
      )}

      {/* Bottom tab bar */}
      {mode !== "ringing" && (
        <div style={{
          position: "absolute", bottom: 0, left: 0, right: 0,
          padding: "8px 8px 20px", display: "flex",
          justifyContent: "space-between", alignItems: "flex-start",
          borderTop: "1px solid rgba(255,255,255,0.08)",
          background: "rgba(0,0,0,0.88)",
          zIndex: 4,
        }}>
          {[
            { label: "Home",    Icon: TabHome,  active: false },
            { label: "Pulse",   Icon: TabPulse, active: true },
            { label: "Ghosted", Icon: TabGhost, active: false },
            { label: "Music",   Icon: TabMusic, active: false },
            { label: "Shop",    Icon: TabShop,  active: false },
            { label: "More",    Icon: TabMore,  active: false },
          ].map(({ label, Icon, active }) => {
            const color = active ? "var(--gold)" : "rgba(255,255,255,0.55)";
            return (
              <div key={label} style={{
                display: "flex", flexDirection: "column", alignItems: "center", gap: 3,
                color: color, flex: 1, minWidth: 0,
              }}>
                {active ? (
                  <div style={{
                    width: 30, height: 30, borderRadius: "50%",
                    border: "1.5px solid var(--gold)",
                    display: "flex", alignItems: "center", justifyContent: "center",
                    boxShadow: "0 0 10px rgba(200,150,44,0.30)",
                  }}>
                    <Icon color={color} />
                  </div>
                ) : (
                  <div style={{ width: 30, height: 30, display: "flex", alignItems: "center", justifyContent: "center" }}>
                    <Icon color={color} />
                  </div>
                )}
                <span style={{
                  fontFamily: "var(--sans)",
                  fontSize: 10,
                  letterSpacing: "0.01em",
                  textTransform: "none",
                  fontWeight: 400,
                }}>{label}</span>
              </div>
            );
          })}
        </div>
      )}

      {/* Ringing screen (fake call) — matches iOS style */}
      {mode === "ringing" && (
        <div style={{ position: "absolute", inset: 0, background: "linear-gradient(to bottom, #0a0a0d 0%, #000 60%)", color: "#fff", padding: "92px 22px 36px", display: "flex", flexDirection: "column", alignItems: "center", textAlign: "center", zIndex: 6 }}>
          <div className="mono" style={{ color: "rgba(255,255,255,0.55)" }}>INCOMING CALL</div>
          <div style={{ fontFamily: "var(--display)", fontSize: 30, marginTop: 14, letterSpacing: "-0.01em" }}>MUM</div>
          <div className="mono" style={{ color: "rgba(255,255,255,0.55)", marginTop: 4 }}>MOBILE · UK</div>
          <div style={{ width: 96, height: 96, borderRadius: "50%", background: "linear-gradient(135deg, #2a2a30, #0a0a0d)", marginTop: 30, position: "relative", border: "1px solid rgba(255,255,255,0.1)" }}>
            <span style={{ position: "absolute", inset: -8, borderRadius: "50%", border: "2px solid rgba(62,224,122,0.6)", animation: "ring 1.2s ease-out infinite" }}></span>
          </div>
          <div style={{ marginTop: "auto", display: "flex", gap: 36, alignItems: "center" }}>
            <div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 6 }}>
              <div style={{ width: 58, height: 58, borderRadius: "50%", background: "#ff3b30", display: "flex", alignItems: "center", justifyContent: "center" }}>
                <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2.4" strokeLinecap="round"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.96.37 1.9.72 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.91.35 1.85.59 2.81.72A2 2 0 0 1 22 16.92z" transform="rotate(135 12 12)"/></svg>
              </div>
              <span className="mono" style={{ color: "rgba(255,255,255,0.55)" }}>DECLINE</span>
            </div>
            <div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 6 }}>
              <div style={{ width: 58, height: 58, borderRadius: "50%", background: "#34c759", display: "flex", alignItems: "center", justifyContent: "center" }}>
                <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2.4" strokeLinecap="round"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.96.37 1.9.72 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.91.35 1.85.59 2.81.72A2 2 0 0 1 22 16.92z"/></svg>
              </div>
              <span className="mono" style={{ color: "rgba(255,255,255,0.55)" }}>ACCEPT</span>
            </div>
          </div>
          <style>{`@keyframes ring { from{ transform:scale(1); opacity:1 } to{ transform:scale(1.8); opacity:0 } }`}</style>
        </div>
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// TierDrawer — fixed bottom drawer. Toggles open; shows tier comparison.
function TierDrawer() {
  const [open, setOpen] = useState(false);
  const tiers = [
    { name: "FOUNDING VENUE", price: "£250", cap: 50, blurb: "Standard globe pin · 2 beacons/quarter · 1 NFC + 2 QR Tap-Pack." },
    { name: "FOUNDING SIGNAL", price: "£500", cap: 25, blurb: "Pulsing globe pin · 4 beacons/qtr · 2 NFC + 4 QR · Licensing Letter." },
    { name: "FOUNDING ANCHOR", price: "£1,000", cap: 10, blurb: "Persistent named label · 8+1 beacons/qtr · co-branded Tap-Pack · monthly call · postcode exclusivity." },
    { name: "FOUNDING PROMOTER", price: "£350", cap: 15, blurb: "Movable globe pin · unlimited events · portable NFC fob + QR." },
    { name: "FOUNDING CHAIN", price: "£750", cap: 5, blurb: "Multi-site operator. Base = 3 locations; +£200/extra." },
    { name: "FOUNDING WELLNESS", price: "£400", cap: 10, blurb: "Calm globe pin (recovery-aware) · 1 NFC + QR · Licensing Letter." },
  ];
  return (
    <aside className={`drawer ${open ? "open" : ""}`} aria-label="Tier comparison">
      <div className="drawer-handle" onClick={() => setOpen((v) => !v)}>
        <span className="h">▲ TIER COMPARISON · ALL 6 FOUNDING PARTNER LEVELS</span>
        <span className="h" style={{ color: "var(--ink-3)" }}>{open ? "CLOSE ▼" : "OPEN ▲"}</span>
      </div>
      <div className="drawer-body">
        <div className="grid-3">
          {tiers.map((t) => (
            <div key={t.name} className="panel" style={{ padding: 18 }}>
              <div className="mono" style={{ color: "var(--gold)", marginBottom: 6 }}>{t.name}</div>
              <div className="display" style={{ fontSize: 38, color: "var(--ink)", lineHeight: 1 }}>{t.price}</div>
              <div className="mono" style={{ color: "var(--ink-3)", marginTop: 4 }}>CAP · {t.cap} SPOTS</div>
              <p style={{ color: "var(--ink-2)", fontSize: 13, lineHeight: 1.5, marginTop: 12 }}>{t.blurb}</p>
            </div>
          ))}
        </div>
      </div>
    </aside>
  );
}

// Export to window so other Babel scripts can use these
Object.assign(window, {
  Reveal,
  useReveal,
  Wordmark,
  Placeholder,
  SpotsCounter,
  MemberForm,
  PulseGlobe,
  SafetyDemo,
  TierDrawer,
});
