// Interactive global presence map for QTG.
// One integrated instrument: dot-grid world, live "follow-the-sun" daylight band,
// active marker with rich detail card (live local time, focus, lead, handoff peers),
// roster of locations on the right that drives — and is driven by — the map.

const OFFICES = [
  // city, country, region tag, lat, lon, tz (IANA), focus, lead, headcount, hq?
  {city:'Singapore', country:'Singapore',      cc:'SG', flag:'🇸🇬', tag:'HQ',     lat: 1.35,  lon: 103.82, tz:'Asia/Singapore',  focus:'HQ · Product · Platform', lead:'Founders Office', heads: 38, hq:true, tempC: 28},
  {city:'Hong Kong', country:'Hong Kong SAR',  cc:'HK', flag:'🇭🇰', tag:'APAC',   lat: 22.32, lon: 114.17, tz:'Asia/Hong_Kong',  focus:'APAC commercial · Brokers', lead:'APAC GM',         heads: 14, tempC: 21},
  {city:'Jakarta',   country:'Indonesia',      cc:'ID', flag:'🇮🇩', tag:'ENG',    lat:-6.20,  lon: 106.85, tz:'Asia/Jakarta',    focus:'Engineering · Backend',     lead:'Engineering Lead',heads: 22, tempC: 29},
  {city:'Bali',      country:'Indonesia',      cc:'ID', flag:'🇮🇩', tag:'ENG',    lat:-8.65,  lon: 115.22, tz:'Asia/Makassar',   focus:'Engineering · Frontend',    lead:'Frontend Lead',   heads: 11, tempC: 27},
  {city:'London',    country:'United Kingdom', cc:'GB', flag:'🇬🇧', tag:'EMEA',   lat:51.51,  lon: -0.13,  tz:'Europe/London',   focus:'EMEA commercial · Partners',lead:'EMEA GM',         heads:  8, tempC:  9},
  {city:'Prague',    country:'Czech Republic', cc:'CZ', flag:'🇨🇿', tag:'OPS',    lat:50.08,  lon:  14.43, tz:'Europe/Prague',   focus:'Operations · Support',      lead:'Ops Director',    heads: 17, tempC:  4},
  {city:'Tallinn',   country:'Estonia',        cc:'EE', flag:'🇪🇪', tag:'TECH',   lat:59.44,  lon:  24.75, tz:'Europe/Tallinn',  focus:'Core platform engineering', lead:'Platform Lead',   heads:  9, tempC:  2},
  {city:'Limassol',  country:'Cyprus',         cc:'CY', flag:'🇨🇾', tag:'EU/MEA', lat:34.71,  lon:  33.02, tz:'Asia/Nicosia',    focus:'Compliance · MEA accounts', lead:'Regional Director',heads: 6, tempC: 19},
  {city:'Dubai',     country:'United Arab Emirates', cc:'AE', flag:'🇦🇪', tag:'MEA', lat:25.20, lon:  55.27, tz:'Asia/Dubai',    focus:'MEA commercial · Brokers',  lead:'MEA GM',          heads: 7, tempC: 26},
  {city:'Bratislava',country:'Slovakia',       cc:'SK', flag:'🇸🇰', tag:'OPS',    lat:48.15,  lon:  17.11, tz:'Europe/Bratislava',focus:'Operations · QA',          lead:'Ops Lead',        heads: 9, tempC:  5},
];

// Equirectangular projection into a 1000×500 viewBox (2:1).
const W = 1000, H = 500;
const minLat = -58, maxLat = 78;
const project = (lat, lon) => {
  const x = ((lon + 180) / 360) * W;
  const t = (maxLat - lat) / (maxLat - minLat);
  const y = 18 + t * (H - 36);
  return [x, y];
};
const p = (lon, lat) => project(lat, lon);

// Hand-tuned simplified continent outlines (lon, lat). Rough but recognizable.
const CONTINENTS = [
  [
    [-168,66],[-160,70],[-141,70],[-128,70],[-105,70],[-95,72],[-82,74],[-70,72],
    [-62,60],[-58,52],[-55,50],[-67,45],[-70,42],[-74,40],[-76,37],[-81,32],
    [-81,25],[-87,30],[-94,29],[-97,26],[-100,22],[-105,20],[-107,24],[-110,23],
    [-115,30],[-120,33],[-122,38],[-124,42],[-124,48],[-130,53],[-135,57],
    [-140,60],[-150,60],[-160,58],[-165,60],[-168,66]
  ],
  [
    [-99,18],[-95,17],[-90,16],[-86,16],[-83,12],[-81,9],[-78,9],[-77,7],[-82,8],
    [-86,12],[-90,14],[-94,15],[-99,16],[-99,18]
  ],
  [
    [-78,11],[-72,11],[-66,10],[-60,8],[-52,5],[-48,0],[-44,-3],[-39,-7],[-37,-10],
    [-39,-15],[-41,-22],[-45,-23],[-48,-26],[-54,-34],[-58,-38],[-63,-41],[-66,-46],
    [-68,-52],[-72,-54],[-74,-50],[-73,-44],[-72,-38],[-70,-30],[-71,-23],[-72,-17],
    [-77,-12],[-80,-6],[-79,-2],[-78,2],[-76,5],[-77,8],[-78,11]
  ],
  [
    [-50,60],[-43,60],[-30,60],[-22,68],[-20,76],[-30,82],[-50,82],[-60,78],[-58,72],
    [-55,68],[-52,64],[-50,60]
  ],
  [
    [-10,36],[-6,36],[-1,38],[3,40],[8,42],[12,44],[14,40],[18,40],[22,40],[26,38],
    [27,36],[30,36],[34,36],[36,38],[40,42],[42,46],[40,50],[38,55],[40,60],[42,65],
    [40,68],[35,70],[28,70],[22,70],[16,68],[12,66],[8,64],[6,62],[10,58],[12,55],
    [8,54],[4,52],[2,51],[-2,51],[-5,55],[-6,58],[-8,55],[-6,52],[-4,50],[-2,48],
    [-3,46],[-1,44],[-2,42],[-6,42],[-9,40],[-10,36]
  ],
  [
    [-17,15],[-15,20],[-10,25],[-5,30],[0,32],[5,32],[10,33],[15,32],[20,30],[25,32],
    [30,32],[33,30],[35,28],[37,22],[40,16],[44,12],[48,11],[51,11],[51,5],[44,2],
    [42,-2],[40,-6],[40,-12],[40,-18],[36,-22],[32,-26],[28,-32],[22,-34],[18,-34],
    [15,-30],[14,-22],[13,-15],[10,-8],[8,-2],[3,4],[-2,5],[-7,5],[-9,9],[-13,12],
    [-17,15]
  ],
  [
    [35,33],[40,35],[45,38],[50,40],[55,38],[58,30],[55,24],[50,18],[44,16],[38,18],
    [35,22],[33,28],[35,33]
  ],
  [
    [30,55],[40,60],[50,68],[60,72],[70,75],[85,75],[100,76],[115,76],[130,72],[140,68],
    [150,65],[160,62],[170,66],[180,68],[180,60],[165,55],[150,48],[140,42],[135,35],
    [128,35],[122,30],[120,22],[115,18],[108,15],[105,10],[102,5],[100,2],[97,5],
    [94,12],[90,18],[85,20],[78,18],[72,15],[68,22],[60,25],[55,30],[50,35],[45,40],
    [42,45],[40,50],[35,52],[30,55]
  ],
  [
    [68,22],[72,22],[76,18],[78,12],[80,8],[82,12],[85,18],[88,22],[88,26],[80,28],
    [72,28],[68,26],[68,22]
  ],
  [
    [95,5],[100,5],[105,2],[108,-2],[112,-3],[118,-3],[124,-4],[130,-5],[136,-3],
    [142,-3],[148,-6],[150,-9],[140,-10],[130,-9],[122,-9],[115,-9],[108,-8],[100,-2],
    [95,5]
  ],
  [
    [114,-22],[118,-20],[125,-15],[132,-12],[138,-12],[142,-10],[145,-15],[148,-22],
    [150,-28],[148,-35],[143,-38],[138,-36],[130,-32],[120,-32],[114,-30],[114,-22]
  ],
  [
    [172,-34],[176,-37],[178,-41],[174,-42],[170,-42],[167,-46],[167,-48],[170,-46],
    [172,-43],[174,-40],[173,-37],[172,-34]
  ],
];
const projectRing = ring => ring.map(([lon,lat]) => p(lon, lat));

// UTC offset like "+08" / "-04" for an IANA tz right now.
const tzOffsetLabel = (tz) => {
  try {
    const fmt = new Intl.DateTimeFormat('en-GB', { timeZone: tz, timeZoneName:'shortOffset' });
    const parts = fmt.formatToParts(new Date());
    const tzn = parts.find(p=>p.type==='timeZoneName')?.value || '';
    // "GMT+8" / "GMT+05:30" / "GMT"
    const m = tzn.match(/([+-])(\d{1,2})(?::(\d{2}))?/);
    if (!m) return 'UTC';
    const sign = m[1]; const h = parseInt(m[2],10); const mm = m[3];
    return `UTC${sign}${String(h).padStart(2,'0')}${mm && mm !== '00' ? ':'+mm : ''}`;
  } catch { return 'UTC'; }
};

// Live local time for an IANA tz, formatted HH:MM and a "kind" bucket.
const useLocalTime = (tz) => {
  const [now, setNow] = React.useState(() => new Date());
  React.useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 1000);
    return () => clearInterval(id);
  }, []);
  try {
    const fmt = new Intl.DateTimeFormat('en-GB', {
      timeZone: tz, hour: '2-digit', minute: '2-digit', hour12: false,
    });
    const parts = fmt.formatToParts(now);
    const hh = parts.find(p=>p.type==='hour').value;
    const mm = parts.find(p=>p.type==='minute').value;
    const h = parseInt(hh, 10);
    const ss = now.getSeconds();
    let kind = 'night';
    if (h >= 5 && h < 8)  kind = 'dawn';
    else if (h >= 8 && h < 18) kind = 'day';
    else if (h >= 18 && h < 21) kind = 'dusk';
    return { hh, mm, ss, kind, h };
  } catch {
    return { hh:'--', mm:'--', ss:0, kind:'day', h:12 };
  }
};

// Sub-solar longitude (rough): where the sun is overhead right now.
// 360°/24h = 15°/h; sun is at lon = -((UTChours - 12) * 15).
const useSubsolarLon = () => {
  const [t, setT] = React.useState(() => new Date());
  React.useEffect(() => {
    const id = setInterval(() => setT(new Date()), 60_000);
    return () => clearInterval(id);
  }, []);
  const utcH = t.getUTCHours() + t.getUTCMinutes()/60 + t.getUTCSeconds()/3600;
  let lon = -((utcH - 12) * 15);
  if (lon > 180) lon -= 360;
  if (lon < -180) lon += 360;
  return lon;
};

// "On shift" if local hour is 8..20 inclusive.
const onShift = (h) => h >= 8 && h <= 20;

const WorldMap = ({ activeIdx = 0, onSelect = () => {}, onHover = () => {} }) => {
  const [hover, setHover] = React.useState(null);

  const polygons = React.useMemo(() => CONTINENTS.map(projectRing), []);
  const dots = React.useMemo(() => {
    const step = 7;
    const out = [];
    const inside = (x, y, ring) => {
      let inside = false;
      for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
        const xi = ring[i][0], yi = ring[i][1];
        const xj = ring[j][0], yj = ring[j][1];
        const intersect = ((yi > y) !== (yj > y)) &&
          (x < (xj - xi) * (y - yi) / (yj - yi + 1e-9) + xi);
        if (intersect) inside = !inside;
      }
      return inside;
    };
    for (let y = 14; y < H - 14; y += step) {
      for (let x = 4; x < W - 4; x += step) {
        for (const ring of polygons) {
          if (inside(x, y, ring)) { out.push([x, y]); break; }
        }
      }
    }
    return out;
  }, [polygons]);

  const subLon = useSubsolarLon();
  const subX = ((subLon + 180) / 360) * W;
  // Daylight band half-width in our flat projection ~ 90° of longitude
  const bandHalf = (90 / 360) * W;

  const active = OFFICES[activeIdx];
  const [ax, ay] = project(active.lat, active.lon);

  // Show the full world map (no zoom-to-active). The active marker is highlighted
  // via pulse rings; the leader line / card overlay pin to its absolute position.
  const Z = 1;
  const axEff = ax;
  const ayEff = ay;

  return (
    <div className="qtg-worldmap" style={{position:'relative', background:'#0B0F12', border:'1px solid rgba(255,255,255,0.08)', borderRadius:14, overflow:'hidden', aspectRatio:`${W}/${H}`}}>
      <svg viewBox={`0 0 ${W} ${H}`} width="100%" height="100%" style={{display:'block'}}>
        {/* Inner group (kept as a wrapping group for layout parity). */}
        <g>
        <defs>
          <radialGradient id="sunGrad" cx="50%" cy="50%" r="50%">
            <stop offset="0%"  stopColor="#00ffff" stopOpacity="0.14"/>
            <stop offset="55%" stopColor="#00ffff" stopOpacity="0.05"/>
            <stop offset="100%" stopColor="#00ffff" stopOpacity="0"/>
          </radialGradient>
          <radialGradient id="pulseGrad" cx="50%" cy="50%" r="50%">
            <stop offset="0%"  stopColor="#00ffff" stopOpacity="0.85"/>
            <stop offset="60%" stopColor="#00ffff" stopOpacity="0.18"/>
            <stop offset="100%" stopColor="#00ffff" stopOpacity="0"/>
          </radialGradient>
          <filter id="markerGlow" x="-50%" y="-50%" width="200%" height="200%">
            <feGaussianBlur stdDeviation="3" />
          </filter>
          <linearGradient id="arcGrad" x1="0" x2="1" y1="0" y2="0">
            <stop offset="0%"  stopColor="#00ffff" stopOpacity="0"/>
            <stop offset="40%" stopColor="#00ffff" stopOpacity="0.85"/>
            <stop offset="100%" stopColor="#00ffff" stopOpacity="0"/>
          </linearGradient>
        </defs>

        {/* daylight band — sub-solar pillar */}
        <ellipse cx={subX} cy={H/2} rx={bandHalf} ry={H*0.62} fill="url(#sunGrad)"/>
        {/* wrap copy when sun band crosses the antimeridian */}
        {subX < bandHalf && (
          <ellipse cx={subX + W} cy={H/2} rx={bandHalf} ry={H*0.62} fill="url(#sunGrad)"/>
        )}
        {subX > W - bandHalf && (
          <ellipse cx={subX - W} cy={H/2} rx={bandHalf} ry={H*0.62} fill="url(#sunGrad)"/>
        )}

        {/* faint grid */}
        <g opacity="0.6">
          {Array.from({length: 9}).map((_,i) => {
            const x = (i+1) * (W/10);
            return <line key={'gx'+i} x1={x} y1="0" x2={x} y2={H} stroke="rgba(255,255,255,0.05)" strokeDasharray="2 6" />;
          })}
          {Array.from({length: 5}).map((_,i) => {
            const y = (i+1) * (H/6);
            return <line key={'gy'+i} x1="0" y1={y} x2={W} y2={y} stroke="rgba(255,255,255,0.05)" strokeDasharray="2 6" />;
          })}
        </g>

        {/* land dots */}
        <g>
          {dots.map(([x,y],i)=>(
            <circle key={i} cx={x} cy={y} r="0.95" fill="rgba(255,255,255,0.34)" />
          ))}
        </g>

        {/* arcs from active office to all others */}
        {OFFICES.map((o, i) => {
          if (i === activeIdx) return null;
          const [x, y] = project(o.lat, o.lon);
          const mx = (ax + x) / 2;
          const my = (ay + y) / 2 - Math.abs(x - ax) * 0.18 - 28;
          const d = `M ${ax} ${ay} Q ${mx} ${my} ${x} ${y}`;
          return (
            <g key={'arc'+i}>
              <path d={d} stroke="rgba(0,180,180,0.20)" strokeWidth="0.6" fill="none" strokeDasharray="2 4">
                <animate attributeName="stroke-dashoffset" from="0" to="-12" dur="1.6s" repeatCount="indefinite"/>
              </path>
              <circle r="2" fill="#00ffff">
                <animateMotion dur={`${2 + (i % 3) * 0.5}s`} repeatCount="indefinite" path={d}/>
                <animate attributeName="opacity" values="0;1;1;0" keyTimes="0;0.15;0.85;1" dur={`${2 + (i % 3) * 0.5}s`} repeatCount="indefinite"/>
              </circle>
            </g>
          );
        })}

        {/* office markers */}
        {OFFICES.map((o,i) => {
          const [x, y] = project(o.lat, o.lon);
          const isActive = i === activeIdx;
          const isHover = hover === i;
          return (
            <g key={i}
               onMouseEnter={()=>{ setHover(i); onHover(i); }}
               onMouseLeave={()=>{ setHover(null); onHover(null); }}
               onClick={()=>onSelect(i)}
               style={{cursor:'pointer'}}>
              {isActive && (
                <>
                  <circle cx={x} cy={y} r="6" fill="url(#pulseGrad)">
                    <animate attributeName="r" from="6" to="26" dur="1.8s" repeatCount="indefinite"/>
                    <animate attributeName="opacity" from="0.9" to="0" dur="1.8s" repeatCount="indefinite"/>
                  </circle>
                  <circle cx={x} cy={y} r="6" fill="url(#pulseGrad)">
                    <animate attributeName="r" from="6" to="20" dur="1.8s" begin="0.6s" repeatCount="indefinite"/>
                    <animate attributeName="opacity" from="0.7" to="0" dur="1.8s" begin="0.6s" repeatCount="indefinite"/>
                  </circle>
                </>
              )}
              <circle cx={x} cy={y} r={isActive?7:5} fill="#00ffff" filter="url(#markerGlow)" opacity={isActive?0.7:0.4}/>
              <circle cx={x} cy={y} r={isActive?3.6:isHover?3.2:2.5} fill="#0B0F12" stroke="#00ffff" strokeWidth={isActive?1.6:1.2}/>
              {/* hover-only mini label (non-active) */}
              {isHover && !isActive && (() => {
                const labelW = Math.max(56, o.city.length*6.2 + 28);
                const flipLeft = x > W - labelW - 20;
                return (
                  <g transform={`translate(${flipLeft ? x - labelW - 9 : x + 9}, ${y - 11})`} style={{pointerEvents:'none'}}>
                    <rect x="0" y="0" width={labelW} height="22" rx="3" fill="#fff" />
                    <text x="8" y="14" fontFamily="Inter Tight, sans-serif" fontSize="10.5" fill="#0B0F12" fontWeight="500">{o.city}</text>
                    <text x={labelW - 8} y="14" textAnchor="end"
                          fontFamily="JetBrains Mono, monospace" fontSize="8.5" fill="rgba(11,15,18,0.55)" letterSpacing="0.06em">{o.tag}</text>
                  </g>
                );
              })()}
            </g>
          );
        })}
        </g>
      </svg>

      {/* HUD: top-left — selected office daylight + local time + temp */}
      <ActiveHUD office={active}/>

      {/* Active-office leader line: from marker to card */}
      <ActiveLeader px={axEff} py={ayEff}/>

      {/* Active-office detail card pinned to the marker */}
      <ActiveOfficeCard office={active} px={axEff} py={ayEff}/>
    </div>
  );
};

const ActiveHUD = ({ office }) => {
  const t = useLocalTime(office.tz);
  const isDaylight = t.h >= 6 && t.h < 19;
  return (
    <div style={{position:'absolute', top:14, left:14, display:'flex', alignItems:'center', gap:10,
      padding:'6px 12px', background:'rgba(11,15,18,0.7)', backdropFilter:'blur(6px)',
      border:'1px solid rgba(255,255,255,0.10)', borderRadius:100}}>
      <span style={{
        width:6, height:6, borderRadius:'50%',
        background: isDaylight ? '#00ffff' : '#6B7680',
        boxShadow: isDaylight ? '0 0 0 4px rgba(0,255,255,0.18)' : '0 0 0 4px rgba(107,118,128,0.18)',
      }}/>
      <span className="mono" style={{fontSize:10.5, color:'rgba(255,255,255,0.85)', letterSpacing:'0.08em'}}>
        {isDaylight ? 'DAYLIGHT' : 'NIGHT'}
      </span>
      <span style={{width:1, height:10, background:'rgba(255,255,255,0.18)'}}/>
      <span className="mono" style={{fontSize:10.5, color:'rgba(255,255,255,0.65)', letterSpacing:'0.04em', fontVariantNumeric:'tabular-nums'}}>
        {office.city.toUpperCase()} · {t.hh}:{t.mm}
      </span>
      {typeof office.tempC === 'number' && (
        <>
          <span style={{width:1, height:10, background:'rgba(255,255,255,0.18)'}}/>
          <span className="mono" style={{fontSize:10.5, color:'rgba(255,255,255,0.65)', letterSpacing:'0.04em', fontVariantNumeric:'tabular-nums'}}>
            {office.tempC}°C
          </span>
        </>
      )}
    </div>
  );
};

const ActiveLeader = ({ px, py }) => {
  // Single straight diagonal line from the marker to the card's near corner.
  // We measure the container so px-space math (the same the card uses) translates
  // cleanly into the SVG overlay.
  const ref = React.useRef(null);
  const [size, setSize] = React.useState({ w: 0, h: 0 });
  React.useEffect(() => {
    if (!ref.current) return;
    const ro = new ResizeObserver(([entry]) => {
      const r = entry.contentRect;
      setSize({ w: r.width, h: r.height });
    });
    ro.observe(ref.current);
    return () => ro.disconnect();
  }, []);
  const xPct = (px / W) * 100;
  const yPct = (py / H) * 100;
  const left = xPct > 55;
  const top  = yPct > 55;
  const GAP_X = 64, GAP_Y = 28;
  const dirX = left ? -1 : 1;
  const dirY = top  ? -1 : 1;
  // Marker anchor in container px.
  const mx = (xPct / 100) * size.w;
  const my = (yPct / 100) * size.h;
  // Card-corner anchor — same math as ActiveOfficeCard pinning.
  const cx = mx + dirX * GAP_X;
  const cy = my + dirY * GAP_Y;
  return (
    <div ref={ref} className="qtg-map-leader" style={{position:'absolute', inset:0, pointerEvents:'none'}}>
      {size.w > 0 && (
        <svg
          width={size.w} height={size.h}
          style={{position:'absolute', inset:0, overflow:'visible'}}
        >
          <line
            x1={mx} y1={my} x2={cx} y2={cy}
            stroke="rgba(0,255,255,0.55)" strokeWidth="1" strokeDasharray="2 3"
          />
          <circle cx={cx} cy={cy} r="2.5" fill="#00ffff" opacity="0.9"/>
        </svg>
      )}
    </div>
  );
};

const ActiveOfficeCard = ({ office, px, py }) => {
  const t = useLocalTime(office.tz);
  const live = onShift(t.h);
  const cardW = 222;
  // Pin the card offset from the marker, snapped to viewBox %.
  // px/py are in 0..W / 0..H — convert to %.
  const xPct = (px / W) * 100;
  const yPct = (py / H) * 100;
  // Decide which corner so card stays inside.
  const left = xPct > 55;
  const top  = yPct > 55;
  // Larger gap so the marker stays visible on the map.
  const GAP_X = 64; // px from marker, horizontally
  const GAP_Y = 28; // px from marker, vertically
  const style = {
    position:'absolute',
    width: cardW,
    [left ? 'right' : 'left']: `calc(${left ? 100 - xPct : xPct}% + ${GAP_X}px)`,
    [top  ? 'bottom' : 'top']:  `calc(${top  ? 100 - yPct : yPct}% + ${GAP_Y}px)`,
    background:'#0B0F12',
    color:'#fff',
    border:'1px solid rgba(255,255,255,0.08)',
    borderRadius:10,
    padding:'11px 12px 10px',
    boxShadow:'0 12px 32px rgba(11,15,18,0.35)',
    pointerEvents:'none',
  };
  return (
    <div className="qtg-map-card" style={style}>
      <div style={{display:'flex', alignItems:'center', justifyContent:'space-between'}}>
        <div style={{display:'flex', alignItems:'center', gap:7}}>
          <span aria-hidden="true" style={{
            display:'inline-flex', alignItems:'center', justifyContent:'center',
            width:20, height:14, borderRadius:2,
            background:'rgba(255,255,255,0.06)',
            border:'1px solid rgba(255,255,255,0.10)',
            fontSize:11.5, lineHeight:1, overflow:'hidden',
            fontFamily:'"Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Twemoji Mozilla", sans-serif',
          }}>{office.flag}</span>
          <span className="mono" style={{fontSize:9.5, letterSpacing:'0.12em', color:'rgba(255,255,255,0.5)'}}>
            {office.country.toUpperCase()}
          </span>
        </div>
        <span className="mono" style={{fontSize:9, padding:'2px 5px', background: office.hq?'#00ffff':'rgba(255,255,255,0.08)', color: office.hq?'#062019':'rgba(255,255,255,0.7)', borderRadius:3, letterSpacing:'0.08em', fontVariantNumeric:'tabular-nums'}}>
          {office.hq ? 'HQ' : tzOffsetLabel(office.tz)}
        </span>
      </div>
      <div style={{display:'flex', alignItems:'baseline', justifyContent:'space-between', marginTop:6}}>
        <div style={{fontFamily:'var(--font-sans)', fontSize:19, letterSpacing:'-0.02em', fontWeight:500}}>
          {office.city}
        </div>
        <div style={{fontFamily:'var(--font-mono)', fontSize:16, letterSpacing:'-0.01em', color:'#00ffff', fontVariantNumeric:'tabular-nums'}}>
          {t.hh}:{t.mm}
        </div>
      </div>
      <div style={{display:'flex', alignItems:'center', gap:6, marginTop:5}}>
        <span style={{width:5, height:5, borderRadius:'50%', background: live?'#00ffff':'rgba(255,255,255,0.25)', boxShadow: live?'0 0 0 3px rgba(0,255,255,0.18)':'none'}}/>
        <span className="mono" style={{fontSize:9.5, letterSpacing:'0.1em', color: live?'rgba(255,255,255,0.85)':'rgba(255,255,255,0.45)'}}>
          {live ? 'ON SHIFT' : 'OFF HOURS'} · {t.kind.toUpperCase()}
        </span>
      </div>
      <ShiftTimeline t={t} live={live}/>
    </div>
  );
};

// Visualizes today's local shift window (08:00–20:00) with current position
// and counters: time-since-start and time-until-end.
const ShiftTimeline = ({ t, live }) => {
  const SHIFT_START = 8;   // 08:00 local
  const SHIFT_END   = 20;  // 20:00 local
  // Convert current local time to fractional hour.
  const nowH = t.h + (parseInt(t.mm, 10) || 0) / 60;
  let progress, sinceMin, untilMin, label;
  if (live) {
    progress = (nowH - SHIFT_START) / (SHIFT_END - SHIFT_START);
    sinceMin = Math.round((nowH - SHIFT_START) * 60);
    untilMin = Math.round((SHIFT_END - nowH) * 60);
    label = 'ON SHIFT';
  } else {
    // Show until next shift start.
    const nextStart = nowH < SHIFT_START ? SHIFT_START : SHIFT_START + 24;
    progress = 0;
    sinceMin = 0;
    untilMin = Math.round((nextStart - nowH) * 60);
    label = 'STARTS IN';
  }
  const fmt = (mins) => {
    const sign = mins < 0 ? '-' : '';
    const m = Math.abs(mins);
    const h = Math.floor(m / 60);
    const mm = m % 60;
    return `${sign}${h}h ${String(mm).padStart(2,'0')}m`;
  };
  return (
    <div style={{marginTop:12, paddingTop:10, borderTop:'1px solid rgba(255,255,255,0.08)'}}>
      <div style={{display:'flex', justifyContent:'space-between', alignItems:'baseline', gap:8}}>
        <span className="mono" style={{fontSize:9, color:'rgba(255,255,255,0.45)', letterSpacing:'0.06em', whiteSpace:'nowrap'}}>
          {live ? `STARTED ${fmt(sinceMin)} AGO` : `SHIFT ${label}`}
        </span>
        <span className="mono" style={{fontSize:9, color:'#00ffff', letterSpacing:'0.06em', fontVariantNumeric:'tabular-nums', whiteSpace:'nowrap'}}>
          {live ? `ENDS IN ${fmt(untilMin)}` : fmt(untilMin)}
        </span>
      </div>
      <div style={{position:'relative', marginTop:8, height:6, borderRadius:3, background:'rgba(255,255,255,0.08)', overflow:'hidden'}}>
        <div style={{
          position:'absolute', left:0, top:0, bottom:0,
          width: `${Math.max(0, Math.min(1, progress)) * 100}%`,
          background:'linear-gradient(90deg, rgba(0,255,255,0.55), #00ffff)',
        }}/>
        {live && (
          <div style={{
            position:'absolute', left: `calc(${Math.max(0, Math.min(1, progress)) * 100}% - 1px)`,
            top:-2, bottom:-2, width:2, background:'#fff', boxShadow:'0 0 6px rgba(255,255,255,0.7)',
          }}/>
        )}
      </div>
      <div style={{display:'flex', justifyContent:'space-between', marginTop:5}}>
        <span className="mono" style={{fontSize:9, color:'rgba(255,255,255,0.35)', letterSpacing:'0.06em', fontVariantNumeric:'tabular-nums'}}>
          08:00
        </span>
        <span className="mono" style={{fontSize:9, color:'rgba(255,255,255,0.35)', letterSpacing:'0.06em', fontVariantNumeric:'tabular-nums'}}>
          20:00
        </span>
      </div>
    </div>
  );
};

// Compact office card sized for a horizontal grid below the map.
const OfficeCard = ({ office, idx, isActive, onClick, onMouseEnter, onMouseLeave }) => {
  const t = useLocalTime(office.tz);
  const live = onShift(t.h);
  return (
    <button
      onClick={onClick}
      onMouseEnter={onMouseEnter}
      onMouseLeave={onMouseLeave}
      style={{
        display:'flex', flexDirection:'column', gap:6,
        padding:'10px 12px 10px',
        background: isActive ? 'rgba(0,255,255,0.06)' : 'rgba(255,255,255,0.02)',
        color:'#fff',
        border:'1px solid',
        borderColor: isActive ? 'rgba(0,255,255,0.45)' : 'rgba(255,255,255,0.10)',
        borderRadius:10,
        cursor:'pointer',
        textAlign:'left',
        font:'inherit',
        transition:'background .18s ease, border-color .18s ease, transform .18s ease',
        transform: isActive ? 'translateY(-2px)' : 'translateY(0)',
        position:'relative',
        boxShadow: isActive ? '0 8px 24px rgba(0,255,255,0.08)' : 'none',
      }}
    >
      <div style={{display:'flex', alignItems:'center', justifyContent:'space-between', gap:8}}>
        <div style={{display:'flex', alignItems:'center', gap:8, minWidth:0}}>
          <span aria-hidden="true" style={{
            width:22, height:16, borderRadius:3,
            display:'inline-flex', alignItems:'center', justifyContent:'center',
            fontSize:13, lineHeight:1,
            background:'rgba(255,255,255,0.06)',
            border:'1px solid rgba(255,255,255,0.10)',
            overflow:'hidden', flexShrink:0,
            fontFamily:'"Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Twemoji Mozilla", sans-serif',
          }} title={office.country}>{office.flag}</span>
          <span style={{fontSize:15, lineHeight:1.15, letterSpacing:'-0.01em', whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis'}}>{office.city}</span>
          {office.hq && (
            <span className="mono" style={{fontSize:8.5, padding:'1px 5px', background:'#00ffff', color:'#062019', borderRadius:2, letterSpacing:'0.08em', flexShrink:0}}>HQ</span>
          )}
        </div>
      </div>
      <div className="mono" style={{fontSize:9.5, color:'rgba(255,255,255,0.40)', letterSpacing:'0.06em'}}>
        {office.country.toUpperCase()}
      </div>
      <div style={{display:'flex', alignItems:'center', justifyContent:'space-between', marginTop:'auto', paddingTop:6, borderTop:'1px solid rgba(255,255,255,0.06)'}}>
        <span className="mono" style={{fontSize:9.5, color:'rgba(255,255,255,0.40)', letterSpacing:'0.06em', fontVariantNumeric:'tabular-nums'}}>
          {tzOffsetLabel(office.tz)}
        </span>
        <div style={{display:'flex', alignItems:'center', gap:6}}>
          <span style={{
            width:5, height:5, borderRadius:'50%',
            background: live ? '#00ffff' : 'rgba(255,255,255,0.25)',
            boxShadow: live ? '0 0 0 3px rgba(0,255,255,0.18)' : 'none',
          }}/>
          <span className="mono" style={{
            fontSize:11.5,
            color: isActive ? '#00ffff' : 'rgba(255,255,255,0.85)',
            letterSpacing:'0.02em',
            fontVariantNumeric:'tabular-nums',
          }}>{t.hh}:{t.mm}</span>
        </div>
      </div>
    </button>
  );
};

// Top-line "follow the sun" coverage bar — independent of selection.
const CoverageBar = () => {
  // Recompute every minute. For each hour 0..23 (UTC), count offices on shift.
  const [now, setNow] = React.useState(() => new Date());
  React.useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 60_000);
    return () => clearInterval(id);
  }, []);
  const buckets = React.useMemo(() => {
    const out = new Array(24).fill(0);
    for (let h = 0; h < 24; h++) {
      // Make a Date for "today at h:00 UTC"
      const probe = new Date(Date.UTC(
        now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), h, 0, 0
      ));
      for (const o of OFFICES) {
        try {
          const fmt = new Intl.DateTimeFormat('en-GB', { timeZone: o.tz, hour: '2-digit', hour12: false });
          const lh = parseInt(fmt.formatToParts(probe).find(p=>p.type==='hour').value, 10);
          if (onShift(lh)) out[h] += 1;
        } catch {}
      }
    }
    return out;
  }, [now]);
  const utcNow = now.getUTCHours();
  const max = Math.max(...buckets, 1);
  return (
    <div style={{padding:'12px 14px', border:'1px solid rgba(255,255,255,0.10)', borderRadius:8, background:'rgba(255,255,255,0.02)'}}>
      <div style={{display:'flex', justifyContent:'space-between', alignItems:'baseline', gap:12, flexWrap:'wrap'}}>
        <div style={{display:'flex', flexDirection:'column', gap:2, minWidth:0}}>
          <span className="mono" style={{fontSize:10.5, color:'rgba(255,255,255,0.55)', letterSpacing:'0.08em'}}>
            FOLLOW-THE-SUN COVERAGE
          </span>
          <span style={{fontSize:12.5, color:'rgba(255,255,255,0.75)', lineHeight:1.35}}>
            Engineers on shift across all locations, hour-by-hour (UTC).
          </span>
        </div>
        <div style={{display:'flex', flexDirection:'column', gap:2, alignItems:'flex-end'}}>
          <span className="mono" style={{fontSize:10.5, color:'rgba(255,255,255,0.55)', letterSpacing:'0.06em'}}>
            NOW · {String(utcNow).padStart(2,'0')}:00 UTC
          </span>
          <span style={{fontSize:13, color:'#fff', fontVariantNumeric:'tabular-nums'}}>
            <span className="mono" style={{color:'#00ffff'}}>{buckets[utcNow]}</span>
            <span style={{color:'rgba(255,255,255,0.5)'}} className="mono"> / {OFFICES.length}</span>
            <span style={{color:'rgba(255,255,255,0.65)', marginLeft:6, fontSize:11.5}}>locations on shift</span>
          </span>
        </div>
      </div>
      <div style={{display:'grid', gridTemplateColumns:'repeat(24, 1fr)', gap:2, marginTop:10, alignItems:'flex-end', height:28}}>
        {buckets.map((v, h) => {
          const isNow = h === utcNow;
          const heightPct = (v / max) * 100;
          return (
            <div key={h} title={`${String(h).padStart(2,'0')}:00 UTC · ${v} of ${OFFICES.length} on shift`}
                 style={{position:'relative', height:'100%', display:'flex', alignItems:'flex-end'}}>
              <div style={{
                width:'100%',
                height: `${Math.max(heightPct, 6)}%`,
                background: isNow ? '#fff' : (v >= OFFICES.length / 2 ? '#00ffff' : v > 0 ? 'rgba(0,255,255,0.45)' : 'rgba(255,255,255,0.10)'),
                borderRadius:1,
              }}/>
            </div>
          );
        })}
      </div>
      <div style={{display:'grid', gridTemplateColumns:'repeat(24, 1fr)', marginTop:6}}>
        {Array.from({length:24}).map((_,h) => (
          <div key={h} className="mono" style={{
            fontSize:8.5,
            textAlign:'center',
            color: h === utcNow ? '#fff' : 'rgba(255,255,255,0.45)',
            opacity: h % 3 === 0 ? 1 : 0,
          }}>
            {String(h).padStart(2,'0')}
          </div>
        ))}
      </div>
      <div style={{display:'flex', alignItems:'center', gap:14, marginTop:8, paddingTop:8, borderTop:'1px solid rgba(255,255,255,0.06)', flexWrap:'wrap'}}>
        <LegendSwatch color="#fff" label={`Now (${String(utcNow).padStart(2,'0')}:00 UTC)`} />
        <LegendSwatch color="#00ffff" label="Peak coverage (≥50% locations)" />
        <LegendSwatch color="rgba(0,255,255,0.45)" label="Partial coverage" />
        <LegendSwatch color="rgba(255,255,255,0.10)" label="Off hours" />
      </div>
    </div>
  );
};

const LegendSwatch = ({ color, label }) => (
  <div style={{display:'flex', alignItems:'center', gap:6}}>
    <span style={{width:10, height:10, background:color, borderRadius:1, border:'1px solid rgba(255,255,255,0.12)'}}/>
    <span className="mono" style={{fontSize:9.5, color:'rgba(255,255,255,0.55)', letterSpacing:'0.05em'}}>{label}</span>
  </div>
);

// The full integrated section — dark module, map left + 2-col office grid right.
const GlobalMapPanel = () => {
  const [activeIdx, setActiveIdx] = React.useState(0);
  return (
    <div className="qtg-mappanel" style={{
      padding:18,
      background:'#0B0F12',
      border:'1px solid rgba(255,255,255,0.06)',
      borderRadius:16,
      display:'grid',
      gridTemplateColumns:'minmax(0, 1.65fr) minmax(0, 1fr)',
      gap:18,
      alignItems:'stretch',
    }}>
      <div style={{display:'flex', flexDirection:'column', gap:12, minWidth:0}}>
        <WorldMap
          activeIdx={activeIdx}
          onSelect={setActiveIdx}
          onHover={(i)=>{ if (i != null) setActiveIdx(i); }}
        />
        <CoverageBar />
      </div>
      <div className="qtg-officegrid" style={{
        display:'grid',
        gridTemplateColumns:'repeat(2, 1fr)',
        gridAutoRows:'1fr',
        gap:8,
        minWidth:0,
      }}>
        {OFFICES.map((o, i) => (
          <OfficeCard
            key={i}
            office={o}
            idx={i}
            isActive={i === activeIdx}
            onClick={()=>setActiveIdx(i)}
            onMouseEnter={()=>setActiveIdx(i)}
            onMouseLeave={()=>{}}
          />
        ))}
      </div>
    </div>
  );
};

window.WorldMap = WorldMap;
window.OFFICES = OFFICES;
window.GlobalMapPanel = GlobalMapPanel;
