/* eslint-disable */
// SlotSimba — Shared components (cards, nav, hero, particles)

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

/* =====================  ICONS  ===================== */
const Icon = {
  Chevron: (p) => (
    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" {...p}>
      <polyline points="6 9 12 15 18 9" />
    </svg>
  ),
  Arrow: (p) => (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" {...p}>
      <line x1="5" y1="12" x2="19" y2="12" />
      <polyline points="12 5 19 12 12 19" />
    </svg>
  ),
  Play: (p) => (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" {...p}>
      <polygon points="6 3 21 12 6 21 6 3" />
    </svg>
  ),
  Search: (p) => (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" {...p}>
      <circle cx="11" cy="11" r="7" />
      <line x1="21" y1="21" x2="16.65" y2="16.65" />
    </svg>
  ),
  Menu: (p) => (
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" {...p}>
      <line x1="3" y1="7" x2="21" y2="7" />
      <line x1="3" y1="13" x2="21" y2="13" />
      <line x1="3" y1="19" x2="21" y2="19" />
    </svg>
  ),
  Close: (p) => (
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" {...p}>
      <line x1="6" y1="6" x2="18" y2="18" />
      <line x1="18" y1="6" x2="6" y2="18" />
    </svg>
  ),
  Check: (p) => (
    <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" {...p}>
      <polyline points="20 6 9 17 4 12" />
    </svg>
  ),
};

/* =====================  LION MARK  =====================
   Heraldic "Simba" crest — a crowned, front-facing lion head with a
   flame-lock mane, rendered as a symmetric silhouette in the brand
   gradient. Original mark (not a trace). Signature unchanged so every
   call site keeps working; scales from 24px (logo) to 96px (about). */
function LionMark({ size = 32, glow = true, tone = "duo" }) {
  // tone: "duo" -> cyan+pink, "gold" -> gold mono, "mono" -> currentColor
  const c1 = tone === "duo" ? "var(--accent-2)" : tone === "gold" ? "#FFD166" : "currentColor";
  const c2 = tone === "duo" ? "var(--accent-3)" : tone === "gold" ? "#FF9F3D" : "currentColor";
  const gid = `lm-grad-${size}-${tone}`;
  const g = `url(#${gid})`;
  const FACE = "#0a0a14";
  return (
    <svg
      width={size} height={size} viewBox="0 0 100 100"
      style={{ filter: glow ? `drop-shadow(0 0 6px ${c1}3a)` : "none" }}
      aria-label="Simba crest"
    >
      <defs>
        <linearGradient id={gid} x1="0" y1="0" x2="0.35" y2="1">
          <stop offset="0%" stopColor={c1} />
          <stop offset="100%" stopColor={c2} />
        </linearGradient>
      </defs>

      {/* Crown */}
      <path
        d="M39 24 L40.5 12 L45.5 18 L50 7.5 L54.5 18 L59.5 12 L61 24 Z"
        fill={g}
      />
      <circle cx="40.5" cy="11" r="2"   fill={g} />
      <circle cx="50"   cy="6.5" r="2.3" fill={g} />
      <circle cx="59.5" cy="11" r="2"   fill={g} />

      {/* Mane — solid broad flame silhouette */}
      <path
        fill={g}
        d="M50 24
           C 60 22, 64 26, 63 33
           C 70 30, 75 36, 71 43
           C 79 43, 80 52, 74 57
           C 80 60, 78 70, 70 71
           C 73 80, 66 86, 59 84
           C 60 92, 53 96, 50 96
           C 47 96, 40 92, 41 84
           C 34 86, 27 80, 30 71
           C 22 70, 20 60, 26 57
           C 20 52, 21 43, 29 43
           C 25 36, 30 30, 37 33
           C 36 26, 40 22, 50 24 Z"
      />

      {/* Face — dark inner field with a forehead widow's-peak */}
      <path
        fill={FACE}
        d="M37 48 Q37 40 45 40 L50 46 L55 40 Q63 40 63 48
           L63 66 Q63 82 50 86 Q37 82 37 66 Z"
      />

      {/* Brow tufts framing the face */}
      <path d="M38 47 L45 42 L44 50 Z" fill={g} />
      <path d="M62 47 L55 42 L56 50 Z" fill={g} />

      {/* Eyes — angled almonds */}
      <path d="M40 56 Q44 52 48 56 Q44 58.5 40 56 Z" fill={g} />
      <path d="M52 56 Q56 52 60 56 Q56 58.5 52 56 Z" fill={g} />

      {/* Nose + muzzle */}
      <path d="M46 63 L54 63 L50 70 Z" fill={g} />
      <path
        d="M50 70 L50 75 M43 75 Q50 81 57 75"
        fill="none" stroke={g} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
      />
    </svg>
  );
}

/* =====================  PARTICLES  =====================
   Constellation network: dots + soft lines between nearby ones, plus a few
   slow-drifting glow blobs underneath. Lightweight (~60 dots, capped DPR). */
function Particles({ enabled = true, density = 70 }) {
  const ref = useRef(null);
  useEffect(() => {
    if (!enabled) return;
    const canvas = ref.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    let raf, w, h, dpr;

    const dots = [];
    const blobs = [];

    const palette = () => {
      // Read live accent values so the network reacts to palette tweaks.
      const cs = getComputedStyle(document.documentElement);
      return [
        cs.getPropertyValue("--accent-1").trim() || "#39ff14",
        cs.getPropertyValue("--accent-2").trim() || "#00ffff",
        cs.getPropertyValue("--accent-3").trim() || "#ff4fa3",
      ];
    };

    const resize = () => {
      dpr = Math.min(window.devicePixelRatio || 1, 2);
      w = canvas.width = window.innerWidth * dpr;
      h = canvas.height = window.innerHeight * dpr;
      canvas.style.width = window.innerWidth + "px";
      canvas.style.height = window.innerHeight + "px";
    };
    resize();
    window.addEventListener("resize", resize);

    const colors = palette();
    for (let i = 0; i < density; i++) {
      dots.push({
        x: Math.random() * w,
        y: Math.random() * h,
        r: (Math.random() * 1.3 + 0.5) * dpr,
        vx: (Math.random() - 0.5) * 0.22 * dpr,
        vy: (Math.random() - 0.5) * 0.22 * dpr,
        color: colors[Math.floor(Math.random() * colors.length)],
        a: Math.random() * 0.5 + 0.3,
      });
    }
    for (let i = 0; i < 4; i++) {
      blobs.push({
        x: Math.random() * w,
        y: Math.random() * h,
        r: (220 + Math.random() * 180) * dpr,
        vx: (Math.random() - 0.5) * 0.15 * dpr,
        vy: (Math.random() - 0.5) * 0.15 * dpr,
        color: colors[i % colors.length],
        phase: Math.random() * Math.PI * 2,
      });
    }

    const MAX_LINK = 130 * (dpr || 1);
    let t = 0;

    const tick = () => {
      t += 0.005;
      ctx.clearRect(0, 0, w, h);

      // Drifting aurora blobs (additive, very soft)
      ctx.globalCompositeOperation = "lighter";
      for (const b of blobs) {
        b.x += b.vx; b.y += b.vy;
        if (b.x < -b.r) b.x = w + b.r;
        if (b.x > w + b.r) b.x = -b.r;
        if (b.y < -b.r) b.y = h + b.r;
        if (b.y > h + b.r) b.y = -b.r;
        const pulse = 0.06 + 0.04 * Math.sin(t * 2 + b.phase);
        const g = ctx.createRadialGradient(b.x, b.y, 0, b.x, b.y, b.r);
        g.addColorStop(0, hexA(b.color, pulse));
        g.addColorStop(1, hexA(b.color, 0));
        ctx.fillStyle = g;
        ctx.beginPath();
        ctx.arc(b.x, b.y, b.r, 0, Math.PI * 2);
        ctx.fill();
      }
      ctx.globalCompositeOperation = "source-over";

      // Constellation: lines between near dots
      ctx.lineWidth = 0.6 * dpr;
      for (let i = 0; i < dots.length; i++) {
        for (let j = i + 1; j < dots.length; j++) {
          const a = dots[i], b = dots[j];
          const dx = a.x - b.x, dy = a.y - b.y;
          const d2 = dx * dx + dy * dy;
          if (d2 < MAX_LINK * MAX_LINK) {
            const d = Math.sqrt(d2);
            const k = 1 - d / MAX_LINK;
            ctx.strokeStyle = hexA(a.color, k * 0.18);
            ctx.shadowBlur = 0;
            ctx.beginPath();
            ctx.moveTo(a.x, a.y);
            ctx.lineTo(b.x, b.y);
            ctx.stroke();
          }
        }
      }

      // Dots
      for (const d of dots) {
        d.x += d.vx;
        d.y += d.vy;
        if (d.x < 0) d.x = w;
        if (d.x > w) d.x = 0;
        if (d.y < 0) d.y = h;
        if (d.y > h) d.y = 0;
        ctx.beginPath();
        ctx.fillStyle = d.color;
        ctx.globalAlpha = d.a;
        ctx.shadowColor = d.color;
        ctx.shadowBlur = 10;
        ctx.arc(d.x, d.y, d.r, 0, Math.PI * 2);
        ctx.fill();
      }
      ctx.globalAlpha = 1;
      ctx.shadowBlur = 0;

      raf = requestAnimationFrame(tick);
    };
    tick();
    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("resize", resize);
    };
  }, [enabled, density]);

  if (!enabled) return null;
  return <canvas ref={ref} className="particles" />;
}

// Convert a CSS color (hex / named) to rgba(r,g,b,a). Falls back to white.
function hexA(c, a) {
  if (!c) return `rgba(255,255,255,${a})`;
  let s = c.trim();
  if (s.startsWith("#")) {
    let h = s.slice(1);
    if (h.length === 3) h = h.split("").map(x => x + x).join("");
    if (h.length === 6) {
      const n = parseInt(h, 16);
      return `rgba(${(n>>16)&255},${(n>>8)&255},${n&255},${a})`;
    }
  }
  if (s.startsWith("rgb")) {
    return s.replace(/rgba?\(([^)]+)\)/, (_, parts) => {
      const [r,g,b] = parts.split(",").map(p => p.trim());
      return `rgba(${r},${g},${b},${a})`;
    });
  }
  return `rgba(255,255,255,${a})`;
}

/* =====================  NAV  ===================== */
function Nav({ route, navigate }) {
  const [mobileOpen, setMobileOpen] = useState(false);
  const [suppressMenus, setSuppressMenus] = useState(false);

  const goto = (path) => {
    setMobileOpen(false);
    navigate(path);
    // Drop focus so :focus-within releases the dropdown.
    if (document.activeElement && document.activeElement.blur) {
      document.activeElement.blur();
    }
    // Hover doesn't release until the cursor moves, so the menu stays
    // expanded after click. Suppress dropdowns until the cursor moves
    // (the user has committed; their cursor will naturally drift).
    setSuppressMenus(true);
    const clear = () => {
      setSuppressMenus(false);
      window.removeEventListener("mousemove", clear);
    };
    window.addEventListener("mousemove", clear);
    setTimeout(clear, 1500); // safety: clear even if cursor never moves
  };
  const isActive = (p) => route.startsWith(p) && p !== "/" ? true : p === "/" && route === "/" ? true : false;

  // Slots grouped by status for the mega-menu (popular / new / upcoming)
  const popular  = window.slotsByStatus("popular");
  const fresh    = window.slotsByStatus("new");
  const upcoming = window.slotsByStatus("upcoming");

  return (
    <header className={`nav ${suppressMenus ? "menus-suppressed" : ""}`}>
      <div className="container nav-inner">
        <a className="logo" href="/" onClick={(e) => { e.preventDefault(); goto("/"); }}>
          <span className="logo-mark logo-mark-lion"><LionMark size={26} /></span>
          <span>slotsimba<span style={{color:"var(--accent-2)"}}>.co.uk</span></span>
        </a>

        <nav className="nav-links">
          <a className={`nav-link ${isActive("/") ? "active":""}`} href="/" onClick={(e)=>{e.preventDefault();goto("/")}}>Home</a>

          {/* SLOTS MEGA-MENU — grouped by status */}
          <div className="dropdown dropdown-mega">
            <a className={`nav-link ${route.startsWith("/slots") ? "active":""}`} href="/slots" onClick={(e)=>{e.preventDefault();goto("/slots")}}>
              Slots <Icon.Chevron />
            </a>
            <div className="dropdown-menu mega-menu">
              <div className="mega-header">
                <a className="mega-all" href="/slots" onClick={(e)=>{e.preventDefault();goto("/slots")}}>
                  <span>All Pragmatic slots</span>
                  <span className="mega-count">{window.SLOTS.length}</span>
                  <Icon.Arrow />
                </a>
                <div className="mega-chips">
                  <a className="mega-chip" href="/slots?feat=Megaways" onClick={(e)=>{e.preventDefault();goto("/slots?feat=Megaways")}}>Megaways</a>
                  <a className="mega-chip" href="/slots?feat=Bonus%20Buy" onClick={(e)=>{e.preventDefault();goto("/slots?feat=Bonus%20Buy")}}>Bonus Buy</a>
                  <a className="mega-chip" href="/slots?feat=Tumble" onClick={(e)=>{e.preventDefault();goto("/slots?feat=Tumble")}}>Tumble</a>
                  <a className="mega-chip" href="/slots?vol=Very" onClick={(e)=>{e.preventDefault();goto("/slots?vol=Very")}}>V-High Vol</a>
                </div>
              </div>
              <div className="mega-cols">
                <div className="mega-col">
                  <a className="mega-col-head" href="/slots?cat=popular" onClick={(e)=>{e.preventDefault();goto("/slots?cat=popular")}}>
                    Popular
                    <span className="mega-col-count">{popular.length}</span>
                  </a>
                  <div className="mega-list">
                    {popular.slice(0, 8).map(s => (
                      <a key={s.id} className="mega-link" href={`/slots/${s.slug}`}
                         onClick={(e)=>{e.preventDefault();goto(`/slots/${s.slug}`)}}>
                        {s.name}
                      </a>
                    ))}
                  </div>
                </div>
                <div className="mega-col">
                  <a className="mega-col-head" href="/slots?cat=new" onClick={(e)=>{e.preventDefault();goto("/slots?cat=new")}}>
                    New releases
                    <span className="mega-col-count">{fresh.length}</span>
                  </a>
                  <div className="mega-list">
                    {fresh.map(s => (
                      <a key={s.id} className="mega-link" href={`/slots/${s.slug}`}
                         onClick={(e)=>{e.preventDefault();goto(`/slots/${s.slug}`)}}>
                        {s.name}
                      </a>
                    ))}
                  </div>
                </div>
                <div className="mega-col">
                  <a className="mega-col-head" href="/slots?cat=upcoming" onClick={(e)=>{e.preventDefault();goto("/slots?cat=upcoming")}}>
                    Upcoming
                    <span className="mega-col-count">{upcoming.length}</span>
                  </a>
                  <div className="mega-list">
                    {upcoming.map(s => (
                      <a key={s.id} className="mega-link" href={`/slots/${s.slug}`}
                         onClick={(e)=>{e.preventDefault();goto(`/slots/${s.slug}`)}}>
                        {s.name}
                      </a>
                    ))}
                  </div>
                </div>
              </div>
            </div>
          </div>

          <a className={`nav-link ${route.startsWith("/slots?cat=new") || route === "/new" ? "active":""}`} href="/slots?cat=new" onClick={(e)=>{e.preventDefault();goto("/slots?cat=new")}}>
            New releases
          </a>
          <a className={`nav-link ${route.startsWith("/slots?cat=upcoming") || route === "/upcoming" ? "active":""}`} href="/slots?cat=upcoming" onClick={(e)=>{e.preventDefault();goto("/slots?cat=upcoming")}}>
            Upcoming
          </a>
          <a className={`nav-link ${isActive("/about") ? "active":""}`} href="/about" onClick={(e)=>{e.preventDefault();goto("/about")}}>About</a>
          <a className={`nav-link ${isActive("/contact") ? "active":""}`} href="/contact" onClick={(e)=>{e.preventDefault();goto("/contact")}}>Contact</a>
        </nav>

        <div className="nav-spacer" />
        <button className="nav-cta" onClick={()=>goto("/slots")}>Browse library</button>
        <button className="hamburger" onClick={()=>setMobileOpen(v=>!v)} aria-label="Menu">
          {mobileOpen ? <Icon.Close/> : <Icon.Menu/>}
        </button>
      </div>

      {mobileOpen && (
        <div className="mobile-menu">
          <a className={`nav-link ${route === "/" ? "active":""}`} href="/" onClick={(e)=>{e.preventDefault();goto("/")}}>Home</a>
          <a className={`nav-link ${route.startsWith("/slots") ? "active":""}`} href="/slots" onClick={(e)=>{e.preventDefault();goto("/slots")}}>All slots</a>
          <a className="nav-link" href="/slots?cat=new" onClick={(e)=>{e.preventDefault();goto("/slots?cat=new")}}>New releases</a>
          <a className="nav-link" href="/slots?cat=upcoming" onClick={(e)=>{e.preventDefault();goto("/slots?cat=upcoming")}}>Upcoming</a>
          <a className={`nav-link ${route === "/about" ? "active":""}`} href="/about" onClick={(e)=>{e.preventDefault();goto("/about")}}>About</a>
          <a className={`nav-link ${route === "/contact" ? "active":""}`} href="/contact" onClick={(e)=>{e.preventDefault();goto("/contact")}}>Contact</a>
        </div>
      )}
    </header>
  );
}

/* =====================  SLOT CARD  ===================== */
function SlotArt({ label, accent = "neon-green", slot }) {
  const accentColor =
    accent === "electric-blue" ? "var(--accent-2)" :
    accent === "hot-pink"      ? "var(--accent-3)" :
                                  "var(--accent-1)";
  // Candidate thumbnail URLs, tried in order: local file -> edge proxy ->
  // raw Pragmatic CDN. Falls back to the stylized text placeholder only if
  // every candidate fails (or none exists, e.g. upcoming titles).
  const chain = window.getSlotThumbChain ? window.getSlotThumbChain(slot) : [];
  const [idx, setIdx] = useState(0);
  // Reset to the first candidate whenever the card switches to a new slot.
  useEffect(() => { setIdx(0); }, [slot && slot.id]);
  const showImage = chain.length > 0 && idx < chain.length;
  // Every thumbnail is a transparent logo — always contained so it sits fully
  // visible inside the card (never cropped).

  return (
    <div className="thumb-art">
      {showImage ? (
        <img
          src={chain[idx]}
          alt={(slot && slot.name) || label}
          className="thumb-img"
          loading="lazy"
          onError={() => setIdx((i) => i + 1)}
        />
      ) : (
        <div className="art-frame" style={{
          borderColor: `color-mix(in oklab, ${accentColor} 40%, transparent)`,
          color: accentColor,
          boxShadow: `inset 0 0 50px color-mix(in oklab, ${accentColor} 14%, transparent)`,
        }}>
          {label}
        </div>
      )}
    </div>
  );
}

function SlotCard({ slot, navigate }) {
  const provider = window.PROVIDERS.find(p => p.id === (slot.providerId || "pragmatic")) || window.PROVIDERS[0];
  const volClass = slot.volatility.toLowerCase().includes("very") ? "pink"
                 : slot.volatility.toLowerCase().includes("high") ? "green"
                 : slot.volatility.toLowerCase().includes("med")  ? "cyan"
                 : "";
  const isNew = slot.status === "new";
  const isUpcoming = slot.status === "upcoming";
  const hasDemo = !!slot.gameSymbol;
  const [demoOpen, setDemoOpen] = useState(false);
  return (
    <>
    <article className={`slot-card ${isUpcoming ? "is-upcoming" : ""}`} onClick={() => navigate(`/slots/${slot.slug}`)}>
      <div className="thumb">
        <SlotArt label={slot.art} accent={slot.accent} slot={slot} />
        {isNew && <div className="status-flag flag-new">NEW</div>}
        {isUpcoming && <div className="status-flag flag-upcoming">UPCOMING · {slot.releaseDate || "TBA"}</div>}
        <div className="play-overlay">
          <button
            className="btn btn-primary btn-sm"
            onClick={(e) => { e.stopPropagation(); setDemoOpen(true); }}
          >
            <Icon.Play /> {hasDemo ? "Play demo" : "Preview"}
          </button>
          <button className="btn btn-ghost btn-sm" style={{marginLeft: 8}}>
            Details <Icon.Arrow />
          </button>
        </div>
      </div>
      <div className="body">
        <div className="provider">{provider?.name}{isUpcoming ? " · upcoming" : isNew ? " · new release" : ""}</div>
        <h3><a className="slot-card-link" href={`/slots/${slot.slug}`} onClick={(e)=>{e.stopPropagation();e.preventDefault();navigate(`/slots/${slot.slug}`)}}>{slot.name}</a></h3>
        <div className="stat-chips">
          <span className={`chip ${volClass}`}>{slot.volatility} vol</span>
          <span className="chip">RTP {slot.rtp}%</span>
        </div>
        <div className="meta-row">
          <span>Max <span className="v">{slot.maxWin}</span></span>
          <span>Reels <span className="v">{slot.reels}</span></span>
        </div>
      </div>
    </article>
    {demoOpen && <DemoModal slot={slot} onClose={() => setDemoOpen(false)} />}
    </>
  );
}

/* =====================  PROVIDER CARD  ===================== */
function ProviderCard({ provider, navigate }) {
  const slotCount = window.slotsByProvider(provider.id).length;
  return (
    <article className="provider-card" onClick={() => navigate(`/providers/${provider.slug}`)}>
      <div className="row spaced">
        <div className="provider-mark" style={{ "--acc": provider.accent }}>
          {provider.initials}
        </div>
        <span className="chip cyan">{provider.catalogueSize} titles</span>
      </div>
      <div>
        <h3>{provider.name}</h3>
        <p className="muted" style={{margin:"6px 0 0", fontSize:13.5, lineHeight:1.5}}>
          {provider.blurb}
        </p>
      </div>
      <div className="meta">
        <span>Founded <span className="v">{provider.founded}</span></span>
        <span>Catalog <span className="v">{slotCount} on site</span></span>
      </div>
    </article>
  );
}

/* =====================  FOOTER  ===================== */
function Footer({ navigate }) {
  return (
    <footer className="footer">
      <div className="container">
        <div className="footer-grid">
          <div>
            <a className="logo" href="/" onClick={(e)=>{e.preventDefault();navigate("/")}}>
              <span className="logo-mark logo-mark-lion"><LionMark size={26} /></span>
              <span>slotsimba<span style={{color:"var(--accent-2)"}}>.co.uk</span></span>
            </a>
            <p style={{
              marginTop:14, fontFamily:"var(--font-mono)", fontSize:11, letterSpacing:".18em",
              textTransform:"uppercase", color:"var(--accent-3)",
            }}>
              ▸ Pragmatic Play · Not on GamStop
            </p>
            <p style={{marginTop:10, fontSize:13, color:"var(--text-3)", maxWidth:380, lineHeight:1.6}}>
              The Pragmatic Play catalogue indexed for the non-GamStop market. Free-play demos, full mechanics, no operator promotion, no bonus listings.
            </p>
          </div>
          <div>
            <h5>Library</h5>
            <div className="footer-list">
              <a href="/slots" onClick={(e)=>{e.preventDefault();navigate("/slots")}}>All slots</a>
              <a href="/slots?cat=popular" onClick={(e)=>{e.preventDefault();navigate("/slots?cat=popular")}}>Popular</a>
              <a href="/slots?cat=new" onClick={(e)=>{e.preventDefault();navigate("/slots?cat=new")}}>New releases</a>
              <a href="/slots?cat=upcoming" onClick={(e)=>{e.preventDefault();navigate("/slots?cat=upcoming")}}>Upcoming</a>
              <a href="/slots?feat=Tumble" onClick={(e)=>{e.preventDefault();navigate("/slots?feat=Tumble")}}>Tumble grids</a>
              <a href="/slots?feat=Megaways" onClick={(e)=>{e.preventDefault();navigate("/slots?feat=Megaways")}}>Megaways</a>
            </div>
          </div>
          <div>
            <h5>Site</h5>
            <div className="footer-list">
              <a href="/pragmatic" onClick={(e)=>{e.preventDefault();navigate("/pragmatic")}}>Studio profile</a>
              <a href="/about" onClick={(e)=>{e.preventDefault();navigate("/about")}}>About</a>
              <a href="/contact" onClick={(e)=>{e.preventDefault();navigate("/contact")}}>Contact</a>
              <a href="#">Editorial policy</a>
            </div>
          </div>
          <div>
            <h5>Responsible play</h5>
            <div className="footer-list">
              <a href="#">GamCare</a>
              <a href="#">BeGambleAware</a>
              <a href="#">Self-exclusion tools</a>
              <a href="#">18+ only</a>
            </div>
          </div>
        </div>
        <div className="footer-bottom">
          <span>© 2026 SlotSimba · For informational purposes only</span>
          <span>v 2.1.0 · UK · EN</span>
        </div>
      </div>
    </footer>
  );
}

/* =====================  DEMO MODAL  =====================
   Two modes:
   - mode "embed": iframe the official Pragmatic Play demo gateway.
   - mode "official": no public demo iframe exists for this studio —
     surface the studio's official catalogue page and explain why. */
function DemoModal({ slot, onClose }) {
  const [loaded, setLoaded] = useState(false);
  const [showFallback, setShowFallback] = useState(false);
  const [fullscreen, setFullscreen] = useState(false);
  const meta = window.getDemoMeta(slot);
  const provider = window.getProvider(slot.providerId) || window.PROVIDERS[0];
  const isEmbed = meta && meta.mode === "embed";
  const isUpcoming = meta && meta.mode === "upcoming";

  // Lock body scroll + handle Escape while the modal is open.
  // Use a ref to keep `onClose` current without rerunning the effect — that
  // way we capture the original body overflow exactly ONCE on mount and
  // restore it exactly once on unmount. Otherwise re-renders that change
  // `onClose`'s identity would each re-capture "hidden" as the "original",
  // permanently locking the page.
  const onCloseRef = useRef(onClose);
  useEffect(() => { onCloseRef.current = onClose; }, [onClose]);
  useEffect(() => {
    const prev = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    const onKey = (e) => { if (e.key === "Escape") onCloseRef.current?.(); };
    window.addEventListener("keydown", onKey);
    return () => {
      window.removeEventListener("keydown", onKey);
      document.body.style.overflow = prev;
    };
  }, []);

  // Reveal an extra fallback if even Pragmatic's iframe doesn't load in 4s.
  useEffect(() => {
    if (!isEmbed) return;
    const t = setTimeout(() => {
      if (!loaded) setShowFallback(true);
    }, 4000);
    return () => clearTimeout(t);
  }, [loaded, isEmbed]);

  return ReactDOM.createPortal(
    <div className="demo-overlay" onClick={onClose}>
      <div
        className={`demo-modal ${fullscreen ? "fullscreen" : ""}`}
        onClick={(e) => e.stopPropagation()}
        role="dialog"
        aria-label={`${slot.name} demo`}
      >
        <header className="demo-head">
          <div className="demo-head-l">
            <div className="demo-eyebrow">
              <span className="demo-dot" /> {isUpcoming ? (slot.status === "new" ? "DEMO PREVIEW · NEW RELEASE" : "UPCOMING · " + (meta.releaseDate || "TBA")) : "DEMO MODE"} · {provider.name.toUpperCase()}
            </div>
            <h3>{slot.name}</h3>
          </div>
          <div className="demo-actions">
            <span className="chip">RTP {slot.rtp}%</span>
            <span className="chip cyan">{slot.volatility} vol</span>
            {isEmbed && (
              <button
                className="demo-icon-btn"
                onClick={() => setFullscreen((v) => !v)}
                aria-label={fullscreen ? "Exit fullscreen" : "Enter fullscreen"}
                title="Fullscreen"
              >
                {fullscreen ? "↙" : "↗"}
              </button>
            )}
            {meta && meta.url && (
              <a
                className="demo-icon-btn"
                href={meta.url}
                target="_blank"
                rel="noopener noreferrer"
                aria-label="Open in new tab"
                title="Open in new tab"
              >
                ⎘
              </a>
            )}
            <button className="demo-close" onClick={onClose} aria-label="Close demo">
              ✕
            </button>
          </div>
        </header>

        <div className="demo-frame-wrap">
          {isEmbed ? (
            <>
              <iframe
                key={meta.url}
                src={meta.url}
                title={`${slot.name} demo`}
                onLoad={() => setLoaded(true)}
                allow="autoplay; fullscreen; encrypted-media"
                referrerPolicy="no-referrer"
              />
              {!loaded && (
                <div className="demo-loader">
                  <div className="demo-loader-ring" />
                  <div className="demo-loader-text">
                    Loading {slot.name}…
                    <div className="demo-loader-sub">
                      Demo served from {meta.source}.
                    </div>
                  </div>
                </div>
              )}
              {showFallback && !loaded && (
                <div className="demo-fallback">
                  <div className="demo-fallback-mark">⎘</div>
                  <h4>Embed didn't load</h4>
                  <p>
                    The demo gateway didn't respond inside the iframe. Open the full demo in a new tab instead — mechanics and bonus features are identical.
                  </p>
                  <a className="btn btn-cyan btn-lg" href={meta.url} target="_blank" rel="noopener noreferrer">
                    Open on {provider.name} ↗
                  </a>
                  <p className="demo-note">
                    Demo play uses fun-money credits only. No real wagering. 18+ · Please play responsibly.
                  </p>
                </div>
              )}
            </>
          ) : isUpcoming ? (
            <UpcomingDemoCard slot={slot} provider={provider} />
          ) : null}
        </div>

        <footer className="demo-foot">
          <div>
            <span className="demo-foot-k">Max win</span>
            <span className="demo-foot-v">{slot.maxWin}</span>
          </div>
          <div>
            <span className="demo-foot-k">Reels</span>
            <span className="demo-foot-v">{slot.reels}</span>
          </div>
          <div>
            <span className="demo-foot-k">Features</span>
            <span className="demo-foot-v">{slot.features.slice(0, 2).join(" · ")}</span>
          </div>
          <div className="demo-foot-warn">⚠ Demo · No real money · 18+</div>
        </footer>
      </div>
    </div>,
    document.body
  );
}

/* Upcoming mode: slot has been announced but Pragmatic hasn't released
   a public demo yet. Show release info + features instead of an iframe. */
function UpcomingDemoCard({ slot, provider }) {
  const isNew = slot.status === "new";
  // For announced-but-not-released titles: show the release-date clock.
  // For released titles whose demo isn't on Pragmatic's public gateway:
  // show a "Demo coming soon" clock and acknowledge the game is already live.
  const clockTop = isNew ? "LIVE" : (slot.releaseDate || "TBA").split(" ")[0];
  const clockBot = isNew ? "Demo soon" : ((slot.releaseDate || "").split(" ")[1] || "Soon");
  const eyebrow  = isNew ? `New release · ${provider.name}` : `Upcoming · ${provider.name}`;
  const headline = isNew
    ? `${slot.name} — demo coming soon`
    : slot.name;
  const explainer = isNew
    ? `${slot.name} is already in operator distribution${slot.releaseYear ? ` (${slot.releaseYear})` : ""}, but Pragmatic hasn't published this title to their public demo gateway yet. The mechanics breakdown below is from the studio's released paytable; the embedded demo will go live here as soon as the public gameSymbol is exposed.`
    : slot.blurb;
  return (
    <div className="demo-upcoming-card">
      <div className="demo-upcoming-art" aria-hidden="true">
        <div className="demo-upcoming-glow" />
        <div className="demo-upcoming-clock">
          <div className="demo-upcoming-clock-ring" />
          <div className="demo-upcoming-clock-label">
            <span>{clockTop}</span>
            <em>{clockBot}</em>
          </div>
        </div>
      </div>
      <div className="demo-upcoming-body">
        <div className="demo-upcoming-eyebrow">{eyebrow}</div>
        <h4>{headline}</h4>
        <p>{explainer}</p>
        {!isNew && (
          <p style={{margin:0, color:"var(--text-3)", fontSize:13.5, lineHeight:1.55}}>{slot.blurb}</p>
        )}
        <div className="demo-upcoming-meta">
          {isNew ? (
            <span><b>Status</b> Live · demo pending</span>
          ) : (
            <span><b>Release</b> {slot.releaseDate || "TBA"}</span>
          )}
          <span><b>RTP</b> {slot.rtp}%</span>
          <span><b>Vol</b> {slot.volatility}</span>
          <span><b>Max win</b> {slot.maxWin}</span>
          <span><b>Reels</b> {slot.reels}</span>
        </div>
        <div className="demo-upcoming-features">
          {slot.features.map((f) => (
            <span key={f} className="chip">{f}</span>
          ))}
        </div>
        <p className="demo-note">
          {isNew
            ? "We don't fake demos — if Pragmatic's gateway doesn't serve a public gameSymbol for this title, you won't see a broken iframe. Demo embeds appear here the moment the symbol is published."
            : "Demo will be embedded here the moment Pragmatic's gateway exposes the gameSymbol. Check back around release."}
        </p>
      </div>
    </div>
  );
}

window.SS_Components = {
  Nav, Footer, Particles, SlotCard, ProviderCard, SlotArt, Icon, LionMark, DemoModal,
};
