/* eslint-disable */
/* cityform — shared components (Brand Design Guidelines v3.0)
   Atoms + Wordmark + Landmarks + City Sticker + cool monochrome map. */

/* ────────────────────────────── icons (line, Ink only) */
const Icon = {
  arrow:   <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M5 12h14M13 6l6 6-6 6"/></svg>,
  arrowL:  <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M19 12H5M11 6l-6 6 6 6"/></svg>,
  search:  <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" strokeWidth="1.5"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>,
  bag:     <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M6 7h12l-1 13H7L6 7zM9 7a3 3 0 0 1 6 0"/></svg>,
  user:    <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="1.5"><circle cx="12" cy="8" r="4"/><path d="M4 21c1.5-4 5-6 8-6s6.5 2 8 6"/></svg>,
  plus:    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M12 5v14M5 12h14"/></svg>,
  minus:   <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M5 12h14"/></svg>,
  check:   <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2"><path d="M5 12l5 5L20 7"/></svg>,
  rotate:  <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M21 12a9 9 0 1 1-3-6.7M21 4v5h-5"/></svg>,
  pin:     <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M12 22s7-7.5 7-13a7 7 0 1 0-14 0c0 5.5 7 13 7 13z"/><circle cx="12" cy="9" r="2.5"/></svg>,
  grid:    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><rect x="4" y="4" width="7" height="7"/><rect x="13" y="4" width="7" height="7"/><rect x="4" y="13" width="7" height="7"/><rect x="13" y="13" width="7" height="7"/></svg>,
  list:    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M8 6h12M8 12h12M8 18h12"/><circle cx="4" cy="6" r="1"/><circle cx="4" cy="12" r="1"/><circle cx="4" cy="18" r="1"/></svg>,
  map:     <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M3 6l6-2 6 2 6-2v14l-6 2-6-2-6 2V6zM9 4v16M15 6v16"/></svg>,
  close:   <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M6 6l12 12M18 6l-12 12"/></svg>,
};

/* ────────────────────────────── Wordmark (§2.1 - §2.3)
   Inter Display 700, lowercase, -3% tracking. Single line. */
function Wordmark({ size = 28, surface = 'auto' }) {
  // auto: pick fill based on data-mode (light → Ink, dark → Mist)
  return (
    <span className="wordmark" style={{
      fontSize: size,
      color: surface === 'reverse' ? 'var(--mist)' : surface === 'bone' ? 'var(--ink)' : 'var(--ink)',
      fontVariationSettings: `'opsz' ${Math.max(14, Math.min(32, size * 0.6))}`,
      display: 'inline-block',
    }}>cityform</span>
  );
}

/* ────────────────────────────── Landmarks (§5.3 / §5.4)
   One flat polygon per city, filled with the city's signature.
   Designed to read at 20 mm minimum. */
function Landmark({ id, color = 'var(--ink)', size = 80 }) {
  // Each landmark is rendered inside a 100×100 viewBox, single fill.
  const shapes = {
    // Sheffield — Park Hill silhouette (deck-access curve abstracted)
    parkhill: (
      <g>
        <path d="M 8 70 L 8 52 Q 30 38 50 44 Q 70 50 92 36 L 92 70 Z" fill={color}/>
        <rect x="8" y="68" width="84" height="6" fill={color}/>
        {/* window slits suggested negatively */}
        <g fill="var(--mist)">
          {[16,28,40,52,64,76].map(x => (
            <rect key={x} x={x} y="58" width="2" height="3"/>
          ))}
          {[16,28,40,52,64,76].map(x => (
            <rect key={'a'+x} x={x} y="50" width="2" height="3"/>
          ))}
        </g>
      </g>
    ),
    // London — Underground Roundel
    roundel: (
      <g>
        <circle cx="50" cy="50" r="38" fill="none" stroke={color} strokeWidth="11"/>
        <rect x="6" y="44" width="88" height="13" fill={color}/>
      </g>
    ),
    // Bath — Royal Crescent (the curve)
    crescent: (
      <g fill={color}>
        <path d="M 10 76 Q 50 18 90 76 L 84 76 Q 50 28 16 76 Z"/>
        {/* columns */}
        {[18,28,38,48,58,68,78].map((x, i) => {
          const a = (i - 3) * 0.32;
          const y0 = 50 + Math.abs(a)*30;
          return <rect key={x} x={x} y={y0} width="3" height={76 - y0}/>;
        })}
      </g>
    ),
    // Cambridge — Mathematical Bridge (trussed timber bridge silhouette)
    mathbridge: (
      <g>
        <path d="M 8 62 Q 50 28 92 62 L 92 66 Q 50 32 8 66 Z" fill={color}/>
        {/* truss members */}
        <g stroke={color} strokeWidth="2.4" fill="none">
          <path d="M 14 64 L 28 40"/>
          <path d="M 28 64 L 42 34"/>
          <path d="M 42 64 L 58 34"/>
          <path d="M 58 64 L 72 34"/>
          <path d="M 72 64 L 86 40"/>
          <path d="M 14 40 L 28 64"/>
          <path d="M 28 34 L 42 64"/>
          <path d="M 42 34 L 58 64"/>
          <path d="M 58 34 L 72 64"/>
          <path d="M 72 40 L 86 64"/>
        </g>
        <rect x="8" y="64" width="84" height="3" fill={color}/>
      </g>
    ),
    // Edinburgh — Castle Rock silhouette
    castle: (
      <g fill={color}>
        {/* basalt rock */}
        <path d="M 8 82 L 8 60 Q 14 48 26 52 L 28 36 L 40 32 L 44 22 L 56 22 L 60 32 L 72 36 L 74 52 Q 86 48 92 60 L 92 82 Z"/>
        {/* crenellations */}
        <g fill="var(--mist)">
          <rect x="30" y="32" width="3" height="4"/>
          <rect x="36" y="32" width="3" height="4"/>
          <rect x="61" y="32" width="3" height="4"/>
          <rect x="67" y="32" width="3" height="4"/>
        </g>
        {/* tower notches */}
        <g fill="var(--mist)">
          <rect x="46" y="22" width="2" height="4"/>
          <rect x="51" y="22" width="2" height="4"/>
        </g>
      </g>
    ),
    // Bristol — Clifton Suspension (towers + suspension cables + deck)
    clifton: (
      <g fill={color}>
        {/* towers */}
        <rect x="14" y="22" width="9" height="48"/>
        <rect x="77" y="22" width="9" height="48"/>
        {/* tower portal caps */}
        <rect x="11" y="22" width="15" height="3"/>
        <rect x="74" y="22" width="15" height="3"/>
        {/* deck */}
        <rect x="8" y="60" width="84" height="6"/>
        {/* main suspension cable */}
        <path d="M 22 26 Q 50 78 78 26" fill="none" stroke={color} strokeWidth="3"/>
        {/* hangers */}
        {[28, 34, 40, 46, 52, 58, 64, 70].map(x => {
          const t = (x - 22) / 56;
          const yCable = 26 + (52 * 4 * t * (1 - t));
          return <line key={x} x1={x} y1={yCable} x2={x} y2="60" stroke={color} strokeWidth="1.2"/>;
        })}
      </g>
    ),
  };
  return (
    <svg viewBox="0 0 100 100" width={size} height={size} style={{ display: 'block' }}>
      {shapes[id] || <circle cx="50" cy="50" r="24" fill={color}/>}
    </svg>
  );
}

/* ────────────────────────────── City Sticker (§5.2)
   80 mm square, Mist background, 0.5 pt Ink edge, centred landmark.
   The ONLY place in the Brand Core where accent colour appears (§3.4 r04). */
function CitySticker({ city, size = 280, variant = 'full' }) {
  if (!city) return null;
  const s = typeof size === 'number' ? size : 280;
  const accentVar = city.accent ? `var(--${city.accent})` : 'var(--stone)';
  const coordStr = `${city.coords.lat.toFixed(4)}° ${city.coords.ns} · ${Math.abs(city.coords.lng).toFixed(4)}° ${city.coords.ew}`;
  const landmarkLabel = city.landmark ? city.landmark.toUpperCase() : 'AWAITING ASSIGNMENT';

  if (variant === 'compact') {
    return (
      <div style={{
        width: s, height: s,
        background: 'var(--mist)',
        border: '1px solid var(--ink)',
        position: 'relative',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        padding: '8%',
      }}>
        <div style={{
          position: 'absolute', top: '8%', left: '8%', right: '8%',
          display: 'flex', justifyContent: 'space-between', alignItems: 'baseline',
        }}>
          <span style={{ fontSize: s * 0.07, fontWeight: 600, letterSpacing: '-0.005em', color: 'var(--ink)', lineHeight: 1 }}>{city.name}</span>
        </div>
        <Landmark id={city.landmarkId} color={accentVar} size={s * 0.42}/>
        <div style={{
          position: 'absolute', bottom: '8%', left: '8%', right: '8%',
          display: 'flex', justifyContent: 'space-between', alignItems: 'baseline',
        }}>
          <span className="wordmark" style={{ fontSize: s * 0.045, color: 'var(--stone)', fontVariationSettings: "'opsz' 14" }}>cityform</span>
          <span style={{ fontSize: s * 0.03, color: 'var(--stone)', letterSpacing: '0.18em' }}>1:11000</span>
        </div>
      </div>
    );
  }

  // Full sticker — proportions are size-relative so it always fits its box.
  const pad = s * 0.06;
  return (
    <div style={{
      width: s, height: s,
      background: 'var(--mist)',
      border: '1px solid var(--ink)',
      position: 'relative',
      padding: `${pad}px ${pad}px ${pad}px ${pad}px`,
      display: 'flex', flexDirection: 'column',
      fontFamily: 'Inter, sans-serif',
      boxSizing: 'border-box',
    }}>
      {/* Title block — top-left */}
      <div>
        <div style={{
          fontWeight: 600, fontSize: s * 0.075, letterSpacing: '-0.005em',
          color: 'var(--ink)', lineHeight: 1,
        }}>{city.name}</div>
        <div style={{
          marginTop: s * 0.012,
          fontWeight: 400, fontSize: s * 0.038,
          letterSpacing: '0.18em', color: 'var(--stone)',
          fontVariantNumeric: 'tabular-nums', lineHeight: 1.2,
        }}>{coordStr}</div>
      </div>

      {/* Landmark — centred, takes remaining space */}
      <div style={{ flex: 1, minHeight: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
        <Landmark id={city.landmarkId} color={accentVar} size={s * 0.42}/>
      </div>

      {/* Landmark label */}
      <div style={{
        textAlign: 'center',
        fontWeight: 500, fontSize: s * 0.033,
        letterSpacing: '0.22em', color: 'var(--stone)',
        textTransform: 'uppercase',
        marginBottom: s * 0.025,
        lineHeight: 1.2,
      }}>{landmarkLabel}</div>

      {/* Hairline + footer */}
      <div style={{ height: 1, background: 'var(--ink)', opacity: 0.4 }}/>
      <div style={{
        marginTop: s * 0.022,
        display: 'flex', justifyContent: 'space-between', alignItems: 'baseline',
      }}>
        <span className="wordmark" style={{ fontSize: s * 0.04, color: 'var(--ink)', fontVariationSettings: "'opsz' 14" }}>cityform</span>
        <span style={{
          fontSize: s * 0.03, color: 'var(--stone)',
          letterSpacing: '0.18em', fontWeight: 400,
        }}>9 CM · 1:11000</span>
      </div>
    </div>
  );
}

/* ────────────────────────────── Inline coordinate caption */
function CoordCaption({ coords, prefix }) {
  const c = coords;
  return (
    <span className="caption" style={{ fontVariantNumeric: 'tabular-nums' }}>
      {prefix && <span>{prefix} &nbsp;·&nbsp; </span>}
      {c.lat.toFixed(4)}° {c.ns} &nbsp;·&nbsp; {Math.abs(c.lng).toFixed(4)}° {c.ew}
    </span>
  );
}

/* ────────────────────────────── Top navigation */
function TopNav({ route, go, cartCount }) {
  const items = [
    { id: 'atlas',   label: 'Collection' },
    { id: 'atelier', label: 'Make yours' },
    { id: 'journal', label: 'Journal' },
    { id: 'about',   label: 'About' },
  ];
  return (
    <div style={{ position:'sticky', top:0, zIndex:50, background:'var(--bg)', borderBottom:'1px solid var(--line)' }}>
      <div className="container" style={{ display:'flex', alignItems:'center', height:72 }}>
        <button onClick={() => go('home')} style={{ marginRight:56 }}>
          <Wordmark size={28}/>
        </button>
        <nav style={{ display:'flex', gap:32 }}>
          {items.map(it => (
            <button key={it.id} onClick={() => go(it.id)}
              style={{
                color: route === it.id ? 'var(--ink)' : 'var(--stone)',
                borderBottom: route === it.id ? '1px solid var(--ink)' : '1px solid transparent',
                paddingBottom: 6,
                transition: 'color .15s, border-color .15s',
                fontWeight: route === it.id ? 600 : 500,
                fontSize: 14,
                letterSpacing: '-0.005em',
              }}>
              {it.label}
            </button>
          ))}
        </nav>
        <div style={{ flex:1 }} />
        <div style={{ display:'flex', alignItems:'center', gap:22, color:'var(--ink-soft)' }}>
          <button title="Search">{Icon.search}</button>
          <button title="Account">{Icon.user}</button>
          <button onClick={() => go('cart')} style={{ display:'flex', alignItems:'center', gap:8 }} title="Bag">
            {Icon.bag}
            <span style={{ fontSize:12, fontVariantNumeric:'tabular-nums', letterSpacing:'0.06em' }}>{cartCount}</span>
          </button>
        </div>
      </div>
    </div>
  );
}

/* ────────────────────────────── ProductCard
   Brand Core only — no accent colour. The CITY MARK miniature
   on the card (when present) is the only place accent lives. */
function ProductCard({ item, onClick, density='standard', variant='photo' }) {
  return (
    <button onClick={onClick} style={{ display:'block', width:'100%', height:'100%', textAlign:'left' }}>
      <div style={{
        background:'var(--bone)',
        border:'1px solid var(--line)',
        overflow:'hidden',
        transition: 'border-color .2s',
        height:'100%',
        display:'flex', flexDirection:'column',
      }}
      onMouseEnter={e => e.currentTarget.style.borderColor='var(--ink)'}
      onMouseLeave={e => e.currentTarget.style.borderColor='var(--line)'}
      >
        {variant === 'sticker' && item.accent ? (
          <div style={{ aspectRatio: '1 / 1', flexShrink:0, padding: '14%', background:'var(--mist)', display:'flex', alignItems:'center', justifyContent:'center' }}>
            <CitySticker city={item} size={300}/>
          </div>
        ) : (
          <div style={{
            aspectRatio: '1 / 1',
            flexShrink:0,
            background:'var(--surface-2)',
            position:'relative',
            display:'flex', alignItems:'center', justifyContent:'center',
          }}>
            <img src={(item.photos && item.photos[0]) || item.photo} alt={item.name}
              style={{
                width: '100%',
                height: '100%',
                objectFit: 'cover',
                filter: 'grayscale(0.4) contrast(1.05)',
              }}/>
            {item.status === 'soon' && (
              <div style={{
                position:'absolute', top:12, left:12,
                background:'var(--ink)', color:'var(--mist)',
                padding:'4px 10px', fontSize:9, letterSpacing:'0.22em', fontWeight:500,
              }}>IN DEVELOPMENT</div>
            )}
          </div>
        )}
        <div style={{ padding: density === 'dense' ? '14px 16px' : '20px 20px' }}>
          <div style={{ display:'flex', justifyContent:'space-between', alignItems:'baseline', gap: 8 }}>
            <div className="h3" style={{
              fontSize: density === 'dense' ? 16 : 18,
              minWidth: 0, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap',
            }}>{item.name}</div>
            <div style={{ fontSize:12, color:'var(--stone)', fontVariantNumeric:'tabular-nums', flexShrink:0 }}>
              {item.status === 'soon' ? '—' : '£95'}
            </div>
          </div>
          <div style={{
            marginTop: 6,
            fontSize: 9, fontWeight: 500,
            letterSpacing: '0.22em', color: 'var(--stone)',
            textTransform: 'uppercase',
            visibility: item.landmark ? 'visible' : 'hidden',
          }}>{item.landmark || '—'}</div>
          <div className="caption" style={{ marginTop:10, fontSize:10 }}>
            {item.coords.lat.toFixed(4)}° {item.coords.ns} · {Math.abs(item.coords.lng).toFixed(4)}° {item.coords.ew}
          </div>
        </div>
      </div>
    </button>
  );
}

/* ────────────────────────────── StyledMap
   Brand Core — Mist background, Ink linework, Stone secondary.
   No accent colours used (§3.4 r04). Single cool style. */

function makeRng(seed) {
  let s = 0;
  for (let i = 0; i < seed.length; i++) s = (s * 31 + seed.charCodeAt(i)) | 0;
  return () => {
    s = (s * 1103515245 + 12345) & 0x7fffffff;
    return s / 0x7fffffff;
  };
}

function generateTile(seed) {
  const rng = makeRng(String(seed));
  const roads = [];
  for (let i = 0; i < 2; i++) {
    const sx = rng() * 1000, sy = rng() * 1000;
    const ex = rng() * 1000, ey = rng() * 1000;
    const cx = (sx + ex)/2 + (rng() - 0.5) * 300;
    const cy = (sy + ey)/2 + (rng() - 0.5) * 300;
    roads.push({ d: `M ${sx},${sy} Q ${cx},${cy} ${ex},${ey}`, w: 6 });
  }
  for (let i = 0; i < 8; i++) {
    const x1 = rng() * 1000, y1 = rng() * 1000;
    const len = 200 + rng() * 500;
    const ang = rng() * Math.PI * 2;
    roads.push({ d: `M ${x1},${y1} L ${x1 + Math.cos(ang) * len},${y1 + Math.sin(ang) * len}`, w: 3 });
  }
  const buildings = [];
  for (let c = 0; c < 4; c++) {
    const cx = 150 + rng() * 700, cy = 150 + rng() * 700;
    const n = 12 + Math.floor(rng() * 16);
    for (let i = 0; i < n; i++) {
      const x = cx + (rng() - 0.5) * 320;
      const y = cy + (rng() - 0.5) * 320;
      const w = 16 + rng() * 30;
      const h = 16 + rng() * 30;
      const r = rng() < 0.3 ? Math.floor(rng() * 4) * 90 : 0;
      if (x > 20 && y > 20 && x < 970 && y < 970) buildings.push({ x, y, w, h, r });
    }
  }
  const hasWater = rng() > 0.45;
  const waterPath = hasWater
    ? `M ${rng() * 200},0 C ${300 + rng() * 200},${200 + rng() * 200} ${500 + rng() * 200},${300 + rng() * 200} ${800 + rng() * 200},1000`
    : null;
  return { roads, buildings, waterPath };
}

/* Project the real OSM geometry returned by /api/storefront/area_preview
   into StyledMap's 0–1000 SVG space. The 2.5 km canvas + metres-per-degree
   match window.bboxToWGS84 exactly, so the rendered streets line up 1:1
   with the draggable bbox overlay. */
function projectArea(area, canvasM = 2500) {
  if (!area || !area.centre) return null;
  const CANVAS_M = canvasM;
  const clat = area.centre.lat, clng = area.centre.lng;
  const mLat = 111320;
  const mLng = 111320 * Math.cos(clat * Math.PI / 180) || 1e-6;
  const P = ([lng, lat]) => [
    (((lng - clng) * mLng) / CANVAS_M + 0.5) * 1000,
    (((clat - lat) * mLat) / CANVAS_M + 0.5) * 1000,
  ];
  const ringToPts = (ring) => ring.map(P).map(([x, y]) => `${x.toFixed(1)},${y.toFixed(1)}`).join(' ');
  const lineToD = (pts) => 'M ' + pts.map(P).map(([x, y]) => `${x.toFixed(1)},${y.toFixed(1)}`).join(' L ');
  const labels = (area.labels || []).map(l => {
    const [x, y] = P([l.lng, l.lat]);
    return { text: l.text, x, y, kind: l.kind };
  }).filter(l => l.x > -40 && l.x < 1040 && l.y > -20 && l.y < 1020);
  return {
    water:     (area.water || []).filter(r => r && r.length >= 3).map(ringToPts),
    buildings: (area.buildings || []).filter(r => r && r.length >= 3).map(ringToPts),
    roads:     (area.roads || []).filter(r => r && r.pts && r.pts.length >= 2)
                 .map(r => ({ d: lineToD(r.pts), w: r.w || 3 })),
    labels,
  };
}

function StyledMap({ seed = 'sheffield', bbox = null, area = null, cropM = null, onTileClick }) {
  const tile = React.useMemo(() => generateTile(seed), [seed]);
  const real = React.useMemo(() => projectArea(area, cropM || 2500), [area, cropM]);
  const hasReal = !!(real && (real.roads.length || real.buildings.length || real.water.length));
  return (
    <div style={{ position:'relative', width:'100%', height:'100%', background:'var(--mist)', overflow:'hidden' }} onClick={onTileClick}>
      <svg viewBox="0 0 1000 1000" preserveAspectRatio="xMidYMid slice" style={{ width:'100%', height:'100%', display:'block' }}>
        {hasReal ? (
          <g style={{ animation: 'cf-fade-in 0.45s ease both' }}>
            {/* Real water — filled Bone polygons */}
            <g>
              {real.water.map((pts, i) => (
                <polygon key={'w'+i} points={pts} fill="#D2D8D2" stroke="none"/>
              ))}
            </g>
            {/* Real roads — Bone fill over Stone hairline edge */}
            <g>
              {real.roads.map((r, i) => (
                <path key={'rb'+i} d={r.d} fill="none" stroke="#B0B6B0" strokeWidth={r.w + 1.5} strokeLinecap="round" strokeLinejoin="round"/>
              ))}
              {real.roads.map((r, i) => (
                <path key={'rf'+i} d={r.d} fill="none" stroke="var(--bone)" strokeWidth={r.w} strokeLinecap="round" strokeLinejoin="round"/>
              ))}
            </g>
            {/* Real buildings — filled Stone tint, Ink hairline */}
            <g>
              {real.buildings.map((pts, i) => (
                <polygon key={'b'+i} points={pts} fill="#CDD2CD" stroke="var(--ink)" strokeWidth="0.5"/>
              ))}
            </g>
            {/* Name labels — brand typography, mist halo for legibility */}
            <g fontFamily="Inter, sans-serif" textAnchor="middle">
              {(real.labels || []).map((l, i) => {
                const isPlace = l.kind === 'place';
                const fs = isPlace ? 17 : 12;
                const txt = isPlace ? l.text.toUpperCase() : l.text;
                return (
                  <text key={'l'+i} x={l.x} y={l.y}
                    fontSize={fs}
                    fontWeight={isPlace ? 600 : 500}
                    letterSpacing={isPlace ? '0.18em' : '0.04em'}
                    fill={isPlace ? 'var(--ink)' : 'var(--stone)'}
                    stroke="var(--mist)" strokeWidth={isPlace ? 4 : 3}
                    paintOrder="stroke" strokeLinejoin="round"
                    opacity={isPlace ? 0.92 : 0.78}>
                    {txt}
                  </text>
                );
              })}
            </g>
          </g>
        ) : (
          <>
        {/* Water — Bone over Mist */}
        {tile.waterPath && (
          <path d={tile.waterPath} fill="none" stroke="#D2D8D2" strokeWidth="50" strokeLinecap="round"/>
        )}
        {/* Roads — Bone, with Stone hairline edge */}
        <g>
          {tile.roads.map((r, i) => (
            <path key={'rb'+i} d={r.d} fill="none" stroke="#B0B6B0" strokeWidth={r.w + 1.5} strokeLinecap="round"/>
          ))}
          {tile.roads.map((r, i) => (
            <path key={'rf'+i} d={r.d} fill="none" stroke="var(--bone)" strokeWidth={r.w} strokeLinecap="round"/>
          ))}
        </g>
        {/* Buildings — filled Stone tint, Ink hairline */}
        <g>
          {tile.buildings.map((b, i) => (
            <rect key={i} x={b.x} y={b.y} width={b.w} height={b.h}
              transform={b.r ? `rotate(${b.r} ${b.x + b.w/2} ${b.y + b.h/2})` : undefined}
              fill="#CDD2CD"
              stroke="var(--ink)"
              strokeWidth="0.5"
            />
          ))}
        </g>
          </>
        )}
        {/* Faint cartographic grid */}
        <g opacity="0.18">
          {[0,200,400,600,800,1000].map(x => (
            <line key={'gv'+x} x1={x} y1="0" x2={x} y2="1000" stroke="var(--ink)" strokeWidth="0.5"/>
          ))}
          {[0,200,400,600,800,1000].map(y => (
            <line key={'gh'+y} x1="0" y1={y} x2="1000" y2={y} stroke="var(--ink)" strokeWidth="0.5"/>
          ))}
        </g>
        {/* North marker */}
        <g transform="translate(940,60)" opacity="0.7">
          <line x1="0" y1="-18" x2="0" y2="18" stroke="var(--ink)" strokeWidth="1"/>
          <path d="M 0 -18 L 4 -10 L 0 -14 L -4 -10 Z" fill="var(--ink)"/>
          <text x="0" y="-24" textAnchor="middle" fontFamily="Inter, sans-serif" fontSize="9" fontWeight="500" letterSpacing="0.22em" fill="var(--ink)">N</text>
        </g>
      </svg>
      {bbox && (
        <div style={{
          position:'absolute',
          left: `${bbox.x}%`, top: `${bbox.y}%`,
          width: `${bbox.w}%`, height: `${bbox.h}%`,
          transform: `rotate(${bbox.r || 0}deg)`,
          transformOrigin: 'center',
          border: '1.5px solid var(--ink)',
          background: 'rgba(26,31,38,0.04)',
          pointerEvents: 'none',
        }}>
          {['tl','tr','bl','br'].map(c => (
            <div key={c} style={{
              position:'absolute',
              width:12, height:12,
              [c.includes('t') ? 'top' : 'bottom']: -6,
              [c.includes('l') ? 'left' : 'right']: -6,
              border: '1.5px solid var(--ink)',
              background: 'var(--bone)',
            }}/>
          ))}
          <div style={{
            position:'absolute', top:-26, left:'50%', transform:'translateX(-50%)',
            fontSize:9, color:'var(--ink)', background:'var(--mist)', padding:'3px 8px',
            letterSpacing:'0.22em', fontWeight:500, whiteSpace:'nowrap',
          }}>9 CM · 1:11000</div>
        </div>
      )}
    </div>
  );
}

Object.assign(window, {
  Icon, Wordmark, Landmark, CitySticker, CoordCaption,
  TopNav, ProductCard, StyledMap, generateTile, makeRng, projectArea,
});
