// map-hero-3d.jsx — JRPG-world-map hero illustration of the 3D mall map.
// Pure SVG. Reads real IMDF polygons from window.FLOORS_GEO and extrudes them
// into chunky isometric blocks. Three skin variants:
//   esports → HD-2D neon (Octopath/P5)
//   cosplay → pastel sticker-glow (softer, warmer)
//   casual  → architectural axonometric (cream lines on charcoal)
//
// Composition: 7 floor plates stacked, 3F Tokyo in dramatic focus,
// others recede w/ atmospheric haze. Player avatar + glowing quest pins.

const ISO_M = [0.866, 0.5, -0.866, 0.5]; // standard 30° isometric

function isoXY(x, y, z = 0) {
  // floor-plan (x,y) → screen (X,Y); z lifts it (negative Y = up)
  return [
    x * ISO_M[0] + y * ISO_M[2],
    x * ISO_M[1] + y * ISO_M[3] - z,
  ];
}

// Find an extreme room on a floor for placing pins
function roomCentroid(room) {
  const cx = room.pts.reduce((s, p) => s + p[0], 0) / room.pts.length;
  const cy = room.pts.reduce((s, p) => s + p[1], 0) / room.pts.length;
  return [cx, cy];
}

// Extrude a polygon into 3 SVG paths: top, front, right (for iso shading).
function extrudePolygon(pts, h, basePtsToScreen, topPtsToScreen) {
  // Top face (the polygon at z = h)
  const top = topPtsToScreen.map(p => p.join(',')).join(' ');
  // Find the bottom-most + right-most edges of the projected polygon
  // for the visible "front" + "right" faces. Simple heuristic:
  // for each edge in the FLOOR plan, compute its iso-projected midpoint;
  // edges whose midpoint is on the lower half of the bbox = front faces.
  const edges = [];
  for (let i = 0; i < pts.length; i++) {
    const a = pts[i], b = pts[(i + 1) % pts.length];
    const aBase = basePtsToScreen[i], bBase = basePtsToScreen[(i + 1) % pts.length];
    const aTop = topPtsToScreen[i], bTop = topPtsToScreen[(i + 1) % pts.length];
    // determine if this is a "visible" face based on edge direction.
    const dx = b[0] - a[0], dy = b[1] - a[1];
    // A polygon edge is on a visible (south or east) face when traveling CCW
    // and dy > 0 (south) or dx > 0 (east). Floor coords have +y down.
    const visible = (dy > 0) || (dx > 0 && dy >= 0) || (dx > 0);
    if (visible) {
      edges.push({ aBase, bBase, aTop, bTop, dx, dy });
    }
  }
  return { top, edges };
}

// Project an array of [x,y] floor-plan points into iso screen coords at given z lift
function projectPts(pts, originX, originY, scale, z) {
  return pts.map(([x, y]) => {
    const [sx, sy] = isoXY(x * scale, y * scale, z);
    return [sx + originX, sy + originY];
  });
}

// ─── HERO MAP ────────────────────────────────────────────────────────
function MapHero3D({ t, ctx, variant = 'jrpg', detail = 'hi' }) {
  // detail='hi' → 2× resolution for hero shot; 'med' → screen-size
  const W = detail === 'hi' ? 358 : 358;
  const H = detail === 'hi' ? 590 : 540;
  const data = window.FLOORS_GEO;
  if (!data) return null;

  const FOCUS = '3F';
  const focusIdx = window.FLOORS.findIndex(f => f.code === FOCUS);

  // Layout: floors stacked vertically, ord 0 = ground; LG below; 5F highest.
  // Vertical screen offset between floors:
  const FLOOR_GAP = 40;       // iso "z" lift between floors (in floor-plan units)
  const SCALE = 0.95;          // floor-plan units → screen units before iso
  const ORIGIN_X = W / 2 - 36;
  const ORIGIN_Y = H * 0.62;

  // Render order: bottom-to-top so upper floors overlap lower
  const sortedFloors = [...window.FLOORS].sort((a, b) => a.ord - b.ord);

  const isCasual = variant === 'arch';
  const ambient = isCasual ? '#0c0d10' : variant === 'cosplay' ? '#2a0e3f' : '#04062a';
  const skyTop = variant === 'cosplay' ? '#5a1c7a' : variant === 'arch' ? '#15171c' : '#1a1a6e';

  return (
    <div style={{ width: W, height: H, position: 'relative',
      background: `radial-gradient(ellipse at 50% 30%, ${skyTop} 0%, ${ambient} 70%)`,
      overflow: 'hidden' }}>
      {/* atmospheric particles / stars */}
      <svg style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }} width={W} height={H}>
        <defs>
          {/* glow filters per variant */}
          <filter id={`pin-glow-${variant}`} x="-50%" y="-50%" width="200%" height="200%">
            <feGaussianBlur stdDeviation={variant === 'arch' ? '0.5' : '2.5'}/>
          </filter>
          <filter id={`floor-glow-${variant}`} x="-20%" y="-20%" width="140%" height="140%">
            <feGaussianBlur stdDeviation="3"/>
          </filter>
          {/* haze gradient — used to fog out unfocused floors */}
          <linearGradient id={`haze-${variant}`} x1="0" y1="0" x2="0" y2="1">
            <stop offset="0" stopColor={ambient} stopOpacity="0.7"/>
            <stop offset="0.5" stopColor={ambient} stopOpacity="0"/>
            <stop offset="1" stopColor={ambient} stopOpacity="0.85"/>
          </linearGradient>
          {/* sparkle */}
          <symbol id={`sparkle-${variant}`} viewBox="-6 -6 12 12">
            <path d="M0 -5L1 -1L5 0L1 1L0 5L-1 1L-5 0L-1 -1Z"
                  fill={t.accent2} opacity="0.9"/>
          </symbol>
          {/* HUAWEI flag pin */}
          <symbol id={`huawei-pin-${variant}`} viewBox="-12 -22 24 24">
            <line x1="0" y1="0" x2="0" y2="-20" stroke={t.huawei} strokeWidth="1.5"/>
            <rect x="0" y="-20" width="14" height="8" fill={t.huawei}/>
            <text x="7" y="-14.5" textAnchor="middle" fontSize="4.5" fontWeight="800"
                  fontFamily={t.fontMono} fill="#fff" letterSpacing="0.04em">HUA</text>
          </symbol>
        </defs>

        {/* ambient stars (esports & cosplay only) */}
        {!isCasual && Array.from({ length: 50 }).map((_, i) => {
          const x = (i * 71) % W;
          const y = (i * 37) % (H * 0.5);
          const r = (i % 3 === 0) ? 1.5 : 0.7;
          return <circle key={`s${i}`} cx={x} cy={y} r={r}
            fill={variant === 'cosplay' ? t.accent2 : t.accent2}
            opacity={(i % 5) / 10 + 0.2}/>;
        })}

        {/* Render every floor, bottom-to-top */}
        {sortedFloors.map((floor, idx) => {
          const fdata = data[floor.code];
          if (!fdata) return null;
          const isFocus = floor.code === FOCUS;
          // Lift relative to focus floor
          const dz = (focusIdx - floor.ord) * FLOOR_GAP;
          // Move floors above focus *up* and floors below *down* on screen
          const z = dz;
          // Atmospheric depth: distance from focus controls opacity
          const distFromFocus = Math.abs(focusIdx - floor.ord);
          const baseOpacity = isFocus ? 1
            : isCasual ? Math.max(0.18, 0.55 - distFromFocus * 0.12)
            : Math.max(0.22, 0.7 - distFromFocus * 0.14);
          const focusBoost = isFocus ? 1 : 0.65;

          // Floor plate dimensions: 160×324 in plan units
          const plateW = 160, plateH = 324;
          // Floor corner points (CCW from bottom-left in plan)
          const platePts = [[0, plateH], [plateW, plateH], [plateW, 0], [0, 0]];
          const baseProjected = projectPts(platePts, ORIGIN_X, ORIGIN_Y, SCALE, z);
          // Top of the slab (10 units thick)
          const SLAB_H = isFocus ? 5 : 4;
          const topProjected = projectPts(platePts, ORIGIN_X, ORIGIN_Y, SCALE, z + SLAB_H);

          return (
            <g key={floor.code} opacity={baseOpacity}>
              {/* Soft drop shadow under each floor */}
              {!isCasual && isFocus && (
                <ellipse cx={baseProjected[0][0] + (baseProjected[1][0] - baseProjected[0][0]) / 2}
                         cy={baseProjected[0][1] + 18}
                         rx={(baseProjected[1][0] - baseProjected[0][0]) / 2 * 1.1}
                         ry={10} fill="#000" opacity="0.6" filter={`url(#floor-glow-${variant})`}/>
              )}

              {/* Slab front face */}
              <polygon
                points={[baseProjected[0], baseProjected[1], topProjected[1], topProjected[0]].map(p => p.join(',')).join(' ')}
                fill={isCasual ? '#1c1f26' : `${floor.color}22`}
                stroke={isCasual ? t.line2 : `${floor.color}88`}
                strokeWidth="0.4"/>
              {/* Slab right face */}
              <polygon
                points={[baseProjected[1], baseProjected[2], topProjected[2], topProjected[1]].map(p => p.join(',')).join(' ')}
                fill={isCasual ? '#15171c' : `${floor.color}33`}
                stroke={isCasual ? t.line2 : `${floor.color}aa`}
                strokeWidth="0.4"/>
              {/* Slab top */}
              <polygon
                points={topProjected.map(p => p.join(',')).join(' ')}
                fill={isCasual ? '#0c0d10' : isFocus ? `${floor.color}1a` : `${floor.color}10`}
                stroke={isCasual ? '#2a2d33' : isFocus ? floor.color : `${floor.color}66`}
                strokeWidth={isCasual ? '0.5' : isFocus ? '0.8' : '0.4'}/>

              {/* Corridor channels (recessed) */}
              {fdata.corridors.slice(0, 6).map((c, i) => {
                const corrTop = projectPts(c.pts, ORIGIN_X, ORIGIN_Y, SCALE, z + SLAB_H + 0.1);
                return (
                  <polygon key={`co${i}`}
                    points={corrTop.map(p => p.join(',')).join(' ')}
                    fill={isCasual ? '#08090b' : `${ambient}cc`}
                    stroke="none"
                    opacity={0.7}/>
                );
              })}

              {/* Extruded room blocks */}
              {fdata.rooms.slice(0, isFocus ? 18 : 8).map((room, i) => {
                // Block height — vary slightly for visual rhythm
                const baseH = isFocus ? 8 : 5;
                const blockH = baseH + (i * 1.7) % (isFocus ? 5 : 3);
                const blockBase = projectPts(room.pts, ORIGIN_X, ORIGIN_Y, SCALE, z + SLAB_H);
                const blockTop = projectPts(room.pts, ORIGIN_X, ORIGIN_Y, SCALE, z + SLAB_H + blockH);

                // Build visible faces
                const faces = [];
                for (let k = 0; k < room.pts.length; k++) {
                  const a = room.pts[k], b = room.pts[(k + 1) % room.pts.length];
                  const dx = b[0] - a[0], dy = b[1] - a[1];
                  // Edge is "visible" if it's on the front/right of the iso projection.
                  // In floor-plan coords (+x right, +y down, CCW polygons),
                  // visible faces have the outward normal pointing into +x or +y.
                  // Edge normal (outward, CCW polygon): (dy, -dx) → so visible when dy>0 or dx<0?
                  // Easier: check projected midpoint y vs polygon centroid y.
                  // Rule of thumb that works: front face if dx>0 (going right), right face if dy>0 (going down)
                  if (dx > 0 || dy > 0) {
                    const aBase = blockBase[k], bBase = blockBase[(k + 1) % room.pts.length];
                    const aTop = blockTop[k], bTop = blockTop[(k + 1) % room.pts.length];
                    // Determine if "front" (south-ish) or "right" (east-ish) for shading
                    const isRight = Math.abs(dx) > Math.abs(dy) ? false : (dy > 0);
                    faces.push({ pts: [aBase, bBase, bTop, aTop], isRight });
                  }
                }

                // Color treatment per variant
                const roomColor = floor.color;
                let topFill, frontFill, rightFill, edgeStroke;
                if (isCasual) {
                  topFill = '#1c1f26';
                  frontFill = '#15171c';
                  rightFill = '#0e1014';
                  edgeStroke = '#3a3d44';
                } else if (variant === 'cosplay') {
                  topFill = `${roomColor}ee`;
                  frontFill = `${roomColor}aa`;
                  rightFill = `${roomColor}77`;
                  edgeStroke = `${roomColor}`;
                } else { // jrpg / esports
                  topFill = isFocus ? roomColor : `${roomColor}cc`;
                  frontFill = isFocus ? `${roomColor}cc` : `${roomColor}88`;
                  rightFill = isFocus ? `${roomColor}88` : `${roomColor}55`;
                  edgeStroke = isFocus ? '#fff' : `${roomColor}`;
                }

                return (
                  <g key={`r${i}`} opacity={focusBoost}>
                    {/* Side faces first */}
                    {faces.map((f, j) => (
                      <polygon key={j}
                        points={f.pts.map(p => p.join(',')).join(' ')}
                        fill={f.isRight ? rightFill : frontFill}
                        stroke={edgeStroke} strokeWidth={isFocus ? '0.35' : '0.25'}
                        strokeOpacity={isCasual ? 0.6 : isFocus ? 0.4 : 0.25}/>
                    ))}
                    {/* Top face — brightest */}
                    <polygon
                      points={blockTop.map(p => p.join(',')).join(' ')}
                      fill={topFill}
                      stroke={edgeStroke} strokeWidth={isFocus ? '0.4' : '0.3'}
                      strokeOpacity={isCasual ? 0.8 : isFocus ? 0.7 : 0.4}/>
                    {/* Tiny window slits on focus floor — JRPG building detail */}
                    {isFocus && !isCasual && i % 2 === 0 && (() => {
                      const [c0, c1, c2, c3] = blockTop.slice(0, 4);
                      if (!c0 || !c1) return null;
                      // Window line on the front face
                      const aF = blockBase[0], bF = blockBase[1] || blockBase[0];
                      const aT = blockTop[0], bT = blockTop[1] || blockTop[0];
                      if (!aF || !bF) return null;
                      const mx = (aF[0] + bF[0]) / 2;
                      const my = (aF[1] + bF[1] + aT[1] + bT[1]) / 4;
                      return (
                        <circle cx={mx} cy={my} r="0.6" fill={t.accent2} opacity="0.9"/>
                      );
                    })()}
                  </g>
                );
              })}

              {/* Floor identifier card on the right */}
              {isFocus && (() => {
                const [lx, ly] = topProjected[2]; // far corner
                return (
                  <g transform={`translate(${lx + 18} ${ly + 8})`}>
                    {/* glow halo */}
                    {!isCasual && (
                      <rect x="-3" y="-12" width="80" height="22" rx="2"
                        fill={floor.color} opacity="0.25" filter={`url(#pin-glow-${variant})`}/>
                    )}
                    <rect x="-3" y="-12" width="80" height="22" rx={isCasual ? '0' : '2'}
                      fill={isCasual ? 'rgba(244,241,234,0.06)' : `${floor.color}33`}
                      stroke={floor.color} strokeWidth={isCasual ? '0.4' : '0.6'}/>
                    {/* corner accent */}
                    <line x1="-3" y1="-12" x2="-3" y2="10" stroke={floor.color} strokeWidth="2"/>
                    <text x="3" y="-3" fontFamily={t.fontMono} fontSize="6" fontWeight="700"
                          fill={floor.color} letterSpacing="0.18em">{floor.code} · TOKYO</text>
                    <text x="3" y="6" fontFamily={t.fontBody} fontSize="4.5" fontWeight="600"
                          fill={t.text} opacity="0.85" letterSpacing="0.02em">Anime + Gaming</text>
                  </g>
                );
              })()}

              {/* Floor label tag for non-focus floors — small, on the right edge */}
              {!isFocus && (() => {
                const [lx, ly] = topProjected[2];
                return (
                  <g transform={`translate(${lx + 12} ${ly + 4})`} opacity={baseOpacity * 1.2}>
                    <text fontFamily={t.fontMono} fontSize="5" fontWeight="700"
                          fill={isCasual ? t.textMute : floor.color}
                          letterSpacing="0.16em">{floor.code}</text>
                    <text x="0" y="6" fontFamily={t.fontMono} fontSize="3.8"
                          fill={t.textDim} letterSpacing="0.08em" opacity="0.7">
                      {floor.name.toUpperCase()}
                    </text>
                  </g>
                );
              })()}

              {/* Quest pins — focus floor only */}
              {isFocus && fdata.rooms.slice(0, 3).map((room, i) => {
                const [cx, cy] = roomCentroid(room);
                const [px, py] = projectPts([[cx, cy]], ORIGIN_X, ORIGIN_Y, SCALE, z + SLAB_H + 12 + i * 0.5)[0];
                const labels = ['Anime Hub', 'Cosplay zone', 'HUAWEI booth'];
                const isHuawei = i === 2 && ctx.huawei;
                const pinColor = isHuawei ? t.huawei : (i === 0 ? t.accent3 : t.accent2);

                return (
                  <g key={`pin${i}`}>
                    {/* drop line to floor */}
                    <line x1={px} y1={py} x2={px} y2={py + 12 + i * 0.5}
                      stroke={pinColor} strokeWidth="0.6" opacity="0.5"
                      strokeDasharray={isCasual ? '1.5 1.5' : 'none'}/>
                    {/* outer glow */}
                    {!isCasual && (
                      <circle cx={px} cy={py} r={isHuawei ? 7 : 5}
                        fill={pinColor} opacity="0.4" filter={`url(#pin-glow-${variant})`}/>
                    )}
                    {/* pin head */}
                    {isCasual ? (
                      // Boarding-pass-tag style pin for casual
                      <g>
                        <rect x={px - 1} y={py - 4} width="14" height="6" rx="0.5"
                          fill="#f4f1ea" stroke={t.text} strokeWidth="0.25"/>
                        <line x1={px} y1={py - 4} x2={px} y2={py + 2}
                          stroke={t.text} strokeWidth="0.4"/>
                        <text x={px + 6} y={py + 0.5} textAnchor="middle"
                          fontFamily={t.fontMono} fontSize="3"
                          fill="#0c0d10" fontWeight="700" letterSpacing="0.08em">
                          {String(i + 1).padStart(2, '0')}
                        </text>
                      </g>
                    ) : isHuawei ? (
                      <use href={`#huawei-pin-${variant}`} x={px} y={py + 2} width="24" height="24"/>
                    ) : (
                      <g>
                        {/* JRPG diamond pin */}
                        <path d={`M${px} ${py - 4} L${px + 3} ${py} L${px} ${py + 4} L${px - 3} ${py} Z`}
                          fill={pinColor} stroke="#fff" strokeWidth="0.5"/>
                        <circle cx={px} cy={py} r="1.2" fill="#fff"/>
                      </g>
                    )}
                    {/* label */}
                    <g transform={`translate(${px + (isHuawei ? -8 : 8)} ${py - 8})`}>
                      <text fontFamily={t.fontMono} fontSize="4" fontWeight="700"
                        fill={isCasual ? t.text : '#fff'} letterSpacing="0.06em"
                        textAnchor={isHuawei ? 'end' : 'start'}
                        style={{ paintOrder: 'stroke', stroke: ambient, strokeWidth: 1.5 }}>
                        {labels[i]}
                      </text>
                    </g>
                  </g>
                );
              })}

              {/* Player avatar marker on focus floor */}
              {isFocus && (() => {
                // Position near a central corridor
                const r = fdata.rooms[5];
                if (!r) return null;
                const [cx, cy] = roomCentroid(r);
                const [px, py] = projectPts([[cx, cy]], ORIGIN_X, ORIGIN_Y, SCALE, z + SLAB_H + 4)[0];
                return (
                  <g>
                    {/* shadow oval */}
                    <ellipse cx={px} cy={py + 4} rx="3" ry="0.8" fill="#000" opacity="0.5"/>
                    {/* boarding-pass character — simple geometric figure */}
                    <g transform={`translate(${px} ${py - 1})`}>
                      {!isCasual && (
                        <circle r="6" fill={t.accent} opacity="0.4" filter={`url(#pin-glow-${variant})`}/>
                      )}
                      {/* body — boarding pass */}
                      <rect x="-2.5" y="-1" width="5" height="3" rx="0.5"
                        fill="#f4f1ea" stroke={t.text} strokeWidth="0.3"/>
                      <line x1="-1.2" y1="-1" x2="-1.2" y2="2" stroke={t.text} strokeWidth="0.2" strokeDasharray="0.4 0.4"/>
                      {/* head */}
                      <circle cx="0" cy="-3" r="1.5" fill={t.text}/>
                      {/* feet */}
                      <line x1="-1" y1="2" x2="-1" y2="3" stroke={t.text} strokeWidth="0.5"/>
                      <line x1="1" y1="2" x2="1" y2="3" stroke={t.text} strokeWidth="0.5"/>
                      {/* "you are here" indicator */}
                      <g transform="translate(0 -8)" opacity={isCasual ? 0.8 : 1}>
                        <path d="M0 0 L-2 -3 L2 -3 Z" fill={t.accent3}/>
                      </g>
                    </g>
                  </g>
                );
              })()}

              {/* Stamp footprints on collected lower floors */}
              {!isFocus && floor.ord < focusIdx && ctx.stamps.has(floor.code) && (() => {
                // Trail of small glowing dots leading toward an exit point
                const trail = fdata.rooms.slice(0, 3).map(roomCentroid);
                return (
                  <g opacity={0.85}>
                    {trail.map((p, i) => {
                      const [px, py] = projectPts([p], ORIGIN_X, ORIGIN_Y, SCALE, z + SLAB_H + 1)[0];
                      return (
                        <g key={`f${i}`}>
                          {!isCasual && (
                            <circle cx={px} cy={py} r="2.5" fill={floor.color}
                              opacity="0.6" filter={`url(#pin-glow-${variant})`}/>
                          )}
                          <circle cx={px} cy={py} r="0.9" fill={isCasual ? floor.color : '#fff'}/>
                        </g>
                      );
                    })}
                  </g>
                );
              })()}
            </g>
          );
        })}

        {/* sparkle particles over focus floor — restrained */}
        {!isCasual && [
          [W * 0.42, H * 0.32], [W * 0.58, H * 0.4],
          [W * 0.5, H * 0.28], [W * 0.62, H * 0.5],
        ].map(([x, y], i) => (
          <use key={`sp${i}`} href={`#sparkle-${variant}`} x={x - 3} y={y - 3} width="6" height="6"
            opacity={0.7}/>
        ))}

        {/* Compass / scale indicator (bottom-left) */}
        <g transform={`translate(20 ${H - 28})`}>
          <text fontFamily={t.fontMono} fontSize="6" fontWeight="700"
            fill={t.textMute} letterSpacing="0.16em">N ↑</text>
          <text x="0" y="9" fontFamily={t.fontMono} fontSize="4"
            fill={t.textDim} letterSpacing="0.1em">1F = 10M</text>
        </g>
      </svg>
    </div>
  );
}

// ─── Hero map screen — uses MapHero3D in place of the placeholder Map3D ────
function ScreenMapHero({ t, ctx, variant, detail = 'med' }) {
  return (
    <PhoneFrame t={t}>
      <div style={{ height: '100%', position: 'relative', background: t.bgDeep, overflow: 'hidden' }}>
        {/* Header */}
        <div style={{ position: 'absolute', top: 50, left: 16, right: 16, zIndex: 5,
          display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <div>
            <SectionLabel t={t} color={t.accent2}>Mall map · live</SectionLabel>
            <PathTitle t={t} size={22} style={{ marginTop: 4 }}>Terminal 21</PathTitle>
          </div>
          <div style={{ background: t.surface, border: `1px solid ${t.line2}`,
            borderRadius: t.edge === 'angular' ? 2 : 100,
            padding: 3, display: 'flex', gap: 2 }}>
            <button style={{ padding: '6px 10px', background: 'transparent', color: t.textMute,
              border: 'none', borderRadius: t.edge === 'angular' ? 2 : 100,
              fontFamily: t.fontMono, fontSize: 10, fontWeight: 700, letterSpacing: '0.1em', cursor: 'pointer' }}>2.5D</button>
            <button style={{ padding: '6px 10px', background: t.accent, color: '#fff',
              border: 'none', borderRadius: t.edge === 'angular' ? 2 : 100,
              fontFamily: t.fontMono, fontSize: 10, fontWeight: 700, letterSpacing: '0.1em', cursor: 'pointer',
              boxShadow: `0 0 12px ${t.accent}66` }}>3D</button>
          </div>
        </div>

        {/* The hero illustration */}
        <div style={{ position: 'absolute', inset: 0, paddingTop: 90, paddingBottom: 90 }}>
          <div style={{ width: '100%', height: '100%', display: 'flex',
            alignItems: 'center', justifyContent: 'center' }}>
            <MapHero3D t={t} ctx={ctx} variant={variant} detail={detail}/>
          </div>
        </div>

        {/* Floor selector (smaller, less obtrusive over hero) */}
        <div style={{ position: 'absolute', right: 12, top: 110, bottom: 110,
          display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 3, zIndex: 5 }}>
          {[...window.FLOORS].reverse().map(f => {
            const isCurrent = f.code === '3F';
            return (
              <div key={f.code} style={{
                width: 36, height: 28, position: 'relative',
                background: isCurrent ? f.color : 'rgba(0,0,0,0.4)',
                border: `1px solid ${isCurrent ? f.color : t.line2}`,
                backdropFilter: 'blur(4px)',
                borderRadius: t.edge === 'angular' ? 2 : 6,
                display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer',
              }}>
                <span style={{ fontFamily: t.fontMono, fontSize: 10, fontWeight: 700,
                  color: isCurrent ? '#fff' : t.text }}>{f.code}</span>
                {ctx.stamps.has(f.code) && (
                  <div style={{ position: 'absolute', top: -3, right: -3, width: 8, height: 8,
                    borderRadius: '50%', background: t.success, border: `1.5px solid ${t.bg}` }}/>
                )}
              </div>
            );
          })}
        </div>

        {/* Detail card */}
        <div style={{ position: 'absolute', left: 16, right: 16, bottom: 100, zIndex: 5 }}>
          <Card t={t} style={{ padding: 14 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
              <div>
                <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                  <div style={{ width: 8, height: 8, borderRadius: '50%', background: '#9B5FFF',
                    boxShadow: variant === 'arch' ? 'none' : `0 0 8px #9B5FFF` }}/>
                  <span style={{ fontFamily: t.fontMono, fontSize: 10, color: '#9B5FFF', letterSpacing: '0.1em' }}>
                    3F · TOKYO · YOU ARE HERE
                  </span>
                </div>
                <div style={{ fontFamily: t.fontBody, fontSize: 15, color: t.text, fontWeight: 600, marginTop: 4 }}>
                  Anime Hub Korat
                </div>
                <div style={{ fontFamily: t.fontMono, fontSize: 10, color: t.textMute, marginTop: 4 }}>
                  3 quests nearby · checkpoint active
                </div>
              </div>
              <button style={{
                background: t.accent, color: '#fff', border: 'none',
                borderRadius: t.edge === 'angular' ? 2 : 100,
                padding: '8px 14px',
                fontFamily: t.fontMono, fontSize: 10, fontWeight: 700, letterSpacing: '0.1em', cursor: 'pointer',
              }}>NAVIGATE →</button>
            </div>
          </Card>
        </div>
      </div>
      <BottomNav t={t} active="map"/>
    </PhoneFrame>
  );
}

Object.assign(window, { MapHero3D, ScreenMapHero });
