/* screens.jsx — Veloroute overlay screens (map is rendered behind by App). */

// ---------- shared bits ----------
function glassStyle(theme, strong) {
  const dark = theme !== 'light';
  return {
    background: dark ? (strong ? 'rgba(22,26,34,0.86)' : 'rgba(26,30,40,0.72)')
                     : (strong ? 'rgba(255,255,255,0.92)' : 'rgba(255,255,255,0.74)'),
    backdropFilter: 'blur(22px) saturate(180%)',
    WebkitBackdropFilter: 'blur(22px) saturate(180%)',
    border: dark ? '0.5px solid rgba(255,255,255,0.1)' : '0.5px solid rgba(0,0,0,0.05)',
  };
}

function RoundBtn({ theme, children, onClick, size = 46, style }) {
  return (
    <button onClick={onClick} className="tap" style={{
      width: size, height: size, borderRadius: size, padding: 0,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      color: 'var(--text)', boxShadow: 'var(--shadow)', cursor: 'pointer',
      transition: 'transform .12s', ...glassStyle(theme, true), ...style,
    }}>{children}</button>
  );
}

function ZoomControl({ theme, style }) {
  return (
    <div style={{
      display: 'flex', flexDirection: 'column', borderRadius: 16, overflow: 'hidden',
      boxShadow: 'var(--shadow)', ...glassStyle(theme, true), ...style,
    }}>
      {[['＋', 1], ['－', -1]].map(([s, d], i) => (
        <button key={i} onClick={() => window.__vrMapZoom && window.__vrMapZoom(d)} className="tap" style={{
          width: 46, height: 44, border: 'none', background: 'none', cursor: 'pointer',
          color: 'var(--text)', fontSize: 21, fontWeight: 500, lineHeight: 1,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          borderTop: i ? '0.5px solid var(--hairline)' : 'none',
        }}>{s}</button>
      ))}
    </div>
  );
}

function SafetyChip({ level, score }) {
  const map = { safe: 'safe', moderate: 'moderate', danger: 'danger' };
  const c = `var(--${map[level]})`;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 5,
      padding: '3px 9px 3px 7px', borderRadius: 999, fontSize: 12.5, fontWeight: 700,
      color: c, background: `color-mix(in oklch, ${c} 15%, transparent)`,
    }}>
      <span style={{ width: 7, height: 7, borderRadius: 9, background: c }} />
      {score}
    </span>
  );
}

const TABS = [
  { id: 'map', label: 'Карта', Icon: window.IconMapPin2 },
  { id: 'routes', label: 'Маршрути', Icon: window.IconRoute },
  { id: 'community', label: 'Общност', Icon: window.IconCommunity },
  { id: 'profile', label: 'Профил', Icon: window.IconUser },
];

function TabBar({ theme, active = 'map', go }) {
  const nav = (id) => go && go(id === 'map' ? 'home' : id);
  return (
    <div style={{
      position: 'absolute', left: 12, right: 12, bottom: 26, height: 64, pointerEvents: 'auto',
      borderRadius: 26, display: 'flex', alignItems: 'center',
      boxShadow: 'var(--shadow-lg)', zIndex: 30, ...glassStyle(theme, true),
    }}>
      {TABS.map(({ id, label, Icon }) => {
        const on = id === active;
        return (
          <button key={id} onClick={() => nav(id)} className="tap" style={{
            flex: 1, background: 'none', border: 'none', cursor: 'pointer',
            display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3,
            color: on ? 'var(--brand)' : 'var(--faint)', padding: '6px 0',
          }}>
            <Icon size={24} sw={on ? 2.1 : 1.8} />
            <span style={{ fontSize: 10.5, fontWeight: on ? 700 : 600, letterSpacing: 0.1 }}>{label}</span>
          </button>
        );
      })}
    </div>
  );
}

// ============================================================
// HOME
// ============================================================
function HomeScreen({ theme, go, onPlanTo, onDirections, onLocate, openReport, community, legendOpen, onToggleLegend, commFilter, onToggleCat, onSetAll, picked, onClosePick, kmz }) {
  const { IconSearch, IconLocateDot, IconLegend, IconWarn, IconUser, IconHome, IconBriefcase } = window;
  const user = window.useUser();
  const initials = user ? window.vrInitials(window.vrDisplayName(user)) : '';
  const [locating, setLocating] = React.useState(false);
  const saved = window.useSaved();
  // up to 2 favourite places for the quick card; fall back to sensible defaults for new users
  const DEFAULT_QUICK = [
    { Icon: IconHome, label: 'Вкъщи', addr: 'ул. Кораб планина · Лозенец', at: [42.6701, 23.3175] },
    { Icon: IconBriefcase, label: 'Работа', addr: 'бул. Цариградско шосе 90 · Изток', at: [42.6709, 23.3559] },
  ];
  const quick = saved.length ? saved.slice(0, 2).map((p) => ({ Icon: window[p.Icon] || window.IconPin, label: p.label, addr: p.addr, at: p.at })) : DEFAULT_QUICK;
  const planSaved = (q) => {
    if (q.at) return onPlanTo && onPlanTo(q.at, q.label, q.addr);
    window.geocodeSearch((q.label || '') + ' София').then((rs) => { if (rs && rs[0]) onPlanTo && onPlanTo(rs[0].at, q.label, q.addr || rs[0].sub); }).catch(() => {});
  };
  const locate = () => {
    if (onLocate) { setLocating(true); Promise.resolve(onLocate()).finally(() => setLocating(false)); }
    else if (window.__vrMapRecenter) window.__vrMapRecenter();
  };
  return (
    <div style={{ position: 'absolute', inset: 0, zIndex: 20, pointerEvents: 'none' }}>
      {/* top search pill */}
      <div style={{ position: 'absolute', top: 64, left: 14, right: 14, zIndex: 25, pointerEvents: 'auto' }}>
        <button onClick={() => go('search')} className="tap" style={{
          width: '100%', height: 56, borderRadius: 20, border: 'none', cursor: 'pointer',
          display: 'flex', alignItems: 'center', gap: 12, padding: '0 8px 0 16px',
          boxShadow: 'var(--shadow-lg)', ...glassStyle(theme, true),
        }}>
          <IconSearch size={22} style={{ color: 'var(--muted)' }} />
          <span style={{ flex: 1, textAlign: 'left', fontSize: 17, fontWeight: 500, color: 'var(--muted)' }}>
            Накъде днес?
          </span>
          <span style={{
            width: 40, height: 40, borderRadius: 40, flexShrink: 0,
            background: 'linear-gradient(140deg, var(--brand), color-mix(in oklch, var(--brand) 60%, #000))',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            color: '#fff', fontWeight: 700, fontSize: 15,
          }}>{initials || <IconUser size={20} />}</span>
        </button>
      </div>

      {/* right controls */}
      <div style={{ position: 'absolute', right: 14, top: 138, display: 'flex', flexDirection: 'column', gap: 12, zIndex: 25, pointerEvents: 'auto' }}>
        <RoundBtn theme={theme} onClick={onToggleLegend} style={legendOpen ? { background: 'var(--brand)' } : undefined}>
          <IconLegend size={22} style={{ color: legendOpen ? '#fff' : 'var(--text)' }} />
        </RoundBtn>
        <RoundBtn theme={theme} onClick={locate} style={locating ? { background: 'var(--brand)' } : undefined}><IconLocateDot size={22} style={{ color: locating ? '#fff' : 'var(--navline)' }} /></RoundBtn>
        <ZoomControl theme={theme} />
      </div>

      {/* community filter / legend panel */}
      {community && legendOpen && <CommunityPanel theme={theme} filter={commFilter} onToggleCat={onToggleCat} onSetAll={onSetAll} onClose={onToggleLegend} kmz={kmz} />}

      {/* report FAB */}
      <button onClick={openReport} className="tap" style={{
        position: 'absolute', right: 14, bottom: 108, zIndex: 28, pointerEvents: 'auto',
        height: 50, borderRadius: 25, border: 'none', cursor: 'pointer',
        display: 'flex', alignItems: 'center', gap: 8, padding: '0 18px 0 14px',
        background: 'var(--danger)', color: '#fff', fontWeight: 700, fontSize: 15,
        boxShadow: '0 6px 20px color-mix(in oklch, var(--danger) 45%, transparent)',
      }}>
        <IconWarn size={21} /> Докладвай
      </button>

      {/* quick saved-places card, lower-left */}
      <div style={{
        position: 'absolute', left: 14, bottom: 108, zIndex: 24, width: 168, pointerEvents: 'auto',
        borderRadius: 20, padding: 4, boxShadow: 'var(--shadow)', ...glassStyle(theme, true),
      }}>
        {quick.map((q, i) => (
          <button key={i} onClick={() => planSaved(q)} className="tap" style={{
            width: '100%', display: 'flex', alignItems: 'center', gap: 11, cursor: 'pointer',
            background: 'none', border: 'none', padding: '9px 11px',
            borderTop: i ? '0.5px solid var(--hairline)' : 'none', color: 'var(--text)',
          }}>
            <span style={{
              width: 34, height: 34, borderRadius: 10, flexShrink: 0,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              background: 'color-mix(in oklch, var(--brand) 14%, transparent)', color: 'var(--brand)',
            }}><q.Icon size={19} /></span>
            <span style={{ textAlign: 'left', lineHeight: 1.2, minWidth: 0 }}>
              <span style={{ display: 'block', fontSize: 14.5, fontWeight: 700 }}>{q.label}</span>
              <span style={{ display: 'block', fontSize: 11.5, color: 'var(--muted)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{q.addr}</span>
            </span>
          </button>
        ))}
      </div>

      {/* community detail sheet */}
      {community && picked && <CommunityDetailSheet theme={theme} item={picked} onClose={onClosePick} go={go} onDirections={onDirections} />}

      <TabBar theme={theme} active="map" go={go} />
    </div>
  );
}

// ============================================================
// SEARCH
// ============================================================
const RECENTS = [
  { name: 'НДК', sub: 'пл. България 1 · Триадица', d: '2,1 км', at: [42.6841, 23.3189], Icon: window.IconMapPin2 },
  { name: 'Борисова градина', sub: 'Главен вход · Лозенец', d: '1,4 км', at: [42.6862, 23.3290], Icon: window.IconLeaf },
];

// One row of the origin/destination selector. Active row shows the live input; inactive shows its label.
function EndpointRow({ kind, label, value, active, onActivate, inputRef, q, setQ }) {
  return (
    <div onClick={!active ? onActivate : undefined} className={active ? '' : 'tap'}
      style={{ display: 'flex', alignItems: 'center', gap: 9, padding: '0 12px', height: 44, cursor: active ? 'text' : 'pointer' }}>
      <span style={{ width: 9, height: 9, borderRadius: '50%', flexShrink: 0,
        background: kind === 'origin' ? 'var(--navline)' : 'var(--danger)' }} />
      <span style={{ fontSize: 12.5, fontWeight: 700, color: 'var(--faint)', width: 24, flexShrink: 0 }}>{label}</span>
      {active ? (
        <input ref={inputRef} value={q} onChange={(e) => setQ(e.target.value)}
          placeholder={kind === 'origin' ? 'Начална точка' : 'Накъде?'}
          style={{ flex: 1, minWidth: 0, border: 'none', outline: 'none', background: 'transparent',
            fontSize: 16, fontWeight: 500, color: 'var(--text)', fontFamily: 'inherit' }} />
      ) : (
        <span style={{ flex: 1, minWidth: 0, fontSize: 16, fontWeight: 600, color: 'var(--text)',
          whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{value}</span>
      )}
    </div>
  );
}

// GSD — REAL address search + origin selection + current location.
// Two endpoints (От/До); the active one is an input with debounced Photon geocoding (Sofia-biased).
// Picking a destination routes immediately; picking/locating an origin keeps you in search.
function SearchScreen({ theme, back, origin, dest, onSetOrigin, onSetDest, onDone }) {
  const { IconBack, IconClose, IconPin } = window;
  // Google-Maps-style flow: "От" defaults to your current location and the cursor lands on "До",
  // so you just search the destination and get routes. You can still tap "От" to change it.
  const [field, setField] = React.useState('dest');
  const [q, setQ] = React.useState('');
  const [results, setResults] = React.useState([]);
  const [loading, setLoading] = React.useState(false);
  const [locating, setLocating] = React.useState(false);
  const [autoLocating, setAutoLocating] = React.useState(false);
  const inputRef = React.useRef(null);

  // Default the origin to the rider's current location (editable) when none is set yet, so the
  // destination is all that's needed. If GPS is denied we leave "От" empty → the show-routes
  // validation then asks for a start.
  React.useEffect(() => {
    if (origin && origin.at) return;
    setAutoLocating(true);
    window.getMyLocation()
      .then((at) => { if (window.__vrShowUserLocation) window.__vrShowUserLocation(at[0], at[1]); onSetOrigin(at, 'Моето местоположение'); })
      .catch(() => { /* leave origin empty; validation handles it */ })
      .finally(() => setAutoLocating(false));
  }, []);   // once per open
  const saved = window.useSaved();
  const recents = window.useRecents();
  const slug = (s) => 'q_' + s.toLowerCase().replace(/[^a-z0-9а-я]+/gi, '_').slice(0, 28);
  const isSaved = (id) => saved.some((x) => x.id === id);
  const toggleSave = (name, sub, at) => window.Saved.toggle({ id: slug(name), Icon: 'IconPin', label: name, addr: sub, at });

  React.useEffect(() => { if (inputRef.current) inputRef.current.focus(); }, [field]);

  // debounced live geocoding for the active field
  React.useEffect(() => {
    const query = q.trim();
    if (query.length < 2) { setResults([]); setLoading(false); return; }
    setLoading(true);
    const t = setTimeout(() => {
      window.geocodeSearch(query)
        .then((rs) => setResults(rs))
        .catch(() => setResults([]))
        .finally(() => setLoading(false));
    }, 300);
    return () => clearTimeout(t);
  }, [q]);

  // route into the right endpoint: origin keeps you here (→ switch to dest), dest routes immediately
  const choose = (at, name, sub) => {
    if (field === 'origin') { onSetOrigin(at, name); setQ(''); setResults([]); setField('dest'); }
    else { onSetDest(at, name, sub); onDone(); }
  };
  const pickResult = (r) => choose(r.at, r.name, r.sub);
  const pickByText = (name, sub) => window.geocodeSearch(name + ' София')
    .then((rs) => { if (rs && rs[0]) choose(rs[0].at, name, sub || rs[0].sub); }).catch(() => {});
  const useMyLocation = () => {
    setLocating(true);
    window.getMyLocation()
      .then((at) => { if (window.__vrShowUserLocation) window.__vrShowUserLocation(at[0], at[1]); onSetOrigin(at, 'Моето местоположение'); setQ(''); setResults([]); setField('dest'); })
      .catch(() => alert('Локацията не е достъпна. Разрешете достъп до местоположението в браузъра.'))
      .finally(() => setLocating(false));
  };

  const showResults = q.trim().length >= 2;
  const ready = origin && origin.at && dest && dest.at;

  return (
    <div style={{ position: 'absolute', inset: 0, zIndex: 40, background: 'var(--bg)', display: 'flex', flexDirection: 'column' }}>
      {/* endpoint selector row */}
      <div style={{ padding: '58px 14px 12px', display: 'flex', alignItems: 'flex-start', gap: 10 }}>
        <button onClick={back} className="tap" style={{ background: 'none', border: 'none', color: 'var(--text)', cursor: 'pointer', padding: 4, marginTop: 22 }}>
          <IconBack size={26} />
        </button>
        <div style={{ flex: 1, borderRadius: 14, background: 'var(--surface-2)', border: '1.5px solid var(--brand)', overflow: 'hidden' }}>
          <EndpointRow kind="origin" label="От" value={origin && origin.name ? origin.name : (autoLocating ? 'Определяне…' : '')} active={field === 'origin'}
            onActivate={() => { setField('origin'); setQ(''); }} inputRef={field === 'origin' ? inputRef : null} q={q} setQ={setQ} />
          <div style={{ height: 1, background: 'var(--hairline)', marginLeft: 30 }} />
          <EndpointRow kind="dest" label="До" value={dest ? dest.name : ''} active={field === 'dest'}
            onActivate={() => { setField('dest'); setQ(''); }} inputRef={field === 'dest' ? inputRef : null} q={q} setQ={setQ} />
        </div>
      </div>

      {/* list */}
      <div className="noscroll" style={{ flex: 1, overflowY: 'auto', padding: '4px 0 8px' }}>
        <ResultRow Icon={window.IconNavArrow || IconPin} accent onClick={useMyLocation}
          name={locating ? 'Определяне…' : 'Моето местоположение'} sub="Използвай текущата позиция" />

        {showResults ? (
          <React.Fragment>
            <SectionLabel>{loading ? 'Търсене…' : 'Резултати'}</SectionLabel>
            {!loading && results.length === 0 && (
              <div style={{ padding: '10px 20px', fontSize: 14, color: 'var(--muted)' }}>Няма резултати за „{q}"</div>
            )}
            {results.map((r, i) => (
              <ResultRow key={r.name + '_' + i} onClick={() => pickResult(r)} Icon={IconPin}
                accent name={r.name} sub={r.sub} d={r.d} delay={i * 50}
                starred={isSaved(slug(r.name))} onStar={() => toggleSave(r.name, r.sub, r.at)} />
            ))}
          </React.Fragment>
        ) : (
          <React.Fragment>
            <SectionLabel>Скорошни</SectionLabel>
            {(recents.length ? recents : RECENTS).map((r) => (
              <ResultRow key={r.name} onClick={() => pickResult(r)} Icon={r.Icon || window.IconMapPin2 || IconPin}
                name={r.name} sub={r.sub} d={r.d}
                starred={isSaved(slug(r.name))} onStar={() => toggleSave(r.name, r.sub, r.at)} />
            ))}

            {saved.length > 0 && <SectionLabel>Запазени</SectionLabel>}
            {saved.map((p) => (
              <ResultRow key={p.id} onClick={() => pickByText(p.label, p.addr)} Icon={window[p.Icon] || IconPin} name={p.label} sub={p.addr}
                starred onStar={() => window.Saved.remove(p.id)} />
            ))}
          </React.Fragment>
        )}
      </div>

      {ready && !showResults && (
        <div style={{ padding: '8px 14px 12px' }}>
          <button onClick={onDone} className="tap" style={{ width: '100%', height: 50, borderRadius: 16, border: 'none',
            background: 'var(--brand)', color: '#fff', fontSize: 16.5, fontWeight: 700, cursor: 'pointer', fontFamily: 'inherit' }}>
            Покажи маршрутите
          </button>
        </div>
      )}
    </div>
  );
}

function SectionLabel({ children }) {
  return <div style={{ padding: '14px 20px 6px', fontSize: 12.5, fontWeight: 700, letterSpacing: 0.4, textTransform: 'uppercase', color: 'var(--faint)' }}>{children}</div>;
}

function ResultRow({ Icon, name, sub, d, onClick, accent, delay = 0, starred, onStar }) {
  return (
    <button onClick={onClick} className="tap" style={{
      width: '100%', background: 'none', border: 'none', cursor: 'pointer',
      display: 'flex', alignItems: 'center', gap: 13, padding: '10px 18px',
      color: 'var(--text)', textAlign: 'left',
    }}>
      <span style={{
        width: 40, height: 40, borderRadius: 12, flexShrink: 0,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        background: accent ? 'color-mix(in oklch, var(--brand) 14%, transparent)' : 'var(--surface-2)',
        color: accent ? 'var(--brand)' : 'var(--muted)',
      }}><Icon size={21} /></span>
      <span style={{ flex: 1, minWidth: 0 }}>
        <span style={{ display: 'block', fontSize: 16, fontWeight: 600, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{name}</span>
        <span style={{ display: 'block', fontSize: 13, color: 'var(--muted)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{sub}</span>
      </span>
      {d && <span style={{ fontSize: 13, fontWeight: 600, color: 'var(--faint)', flexShrink: 0 }}>{d}</span>}
      {onStar && (
        <span onClick={(e) => { e.stopPropagation(); onStar(); }} className="tap" role="button" aria-label="Запази" style={{ flexShrink: 0, marginLeft: 2, marginRight: -4, padding: 6, display: 'flex', color: starred ? 'var(--brand)' : 'var(--faint)', cursor: 'pointer' }}>
          <svg width="20" height="20" viewBox="0 0 24 24" fill={starred ? 'currentColor' : 'none'} stroke="currentColor" strokeWidth="1.7" strokeLinejoin="round"><path d="M12 4l2.4 5 5.6.6-4.2 3.8 1.2 5.6L12 16.8 7 19l1.2-5.6L4 9.6 9.6 9 12 4z"/></svg>
        </span>
      )}
    </button>
  );
}

Object.assign(window, {
  glassStyle, RoundBtn, ZoomControl, SafetyChip, TabBar, HomeScreen, SearchScreen, SectionLabel, ResultRow,
});

// ============================================================
// COMMUNITY LAYER — filter panel + detail sheet
// ============================================================
const CAT_GROUPS = [
  ['Маршрути', ['safe', 'danger']],
  ['Опасности', ['pothole', 'traffic', 'blocked', 'lighting']],
  ['Удобства', ['parking', 'water', 'repair']],
  ['Велоалеи (VELOSOFIZE)', ['bikelane', 'bikelane_shared', 'comfort', 'rough', 'sidewalk', 'untested']],
  ['Транспорт и пресичане', ['transit', 'crossing', 'poi']],
];

// Render the exact same white glyph the map marker uses (window.VR_CMARK_GLYPH, set by
// real-map.jsx) inside the category's coloured square — so legend chips, the detail-sheet
// header and the map markers are visually identical (e.g. parking = blue P, water = drop).
function CatChip({ cat, size = 30, radius = 9 }) {
  const cats = window.VR_COMMUNITY.cats;
  const c = cats[cat] || {};
  const glyph = (window.VR_CMARK_GLYPH || {})[c.glyph] || '';
  return (
    <span style={{
      width: size, height: size, borderRadius: radius, flexShrink: 0,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      background: `var(--${c.color})`, color: '#fff',
    }}>
      <svg width={size * 0.6} height={size * 0.6} viewBox="0 0 24 24" fill="none"
        stroke="#fff" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
        dangerouslySetInnerHTML={{ __html: glyph }} />
    </span>
  );
}

function commCounts() {
  const C = window.VR_COMMUNITY, out = {};
  Object.keys(C.cats).forEach((k) => { out[k] = 0; });
  C.points.forEach((p) => { out[p.cat] = (out[p.cat] || 0) + 1; });
  C.segments.forEach((s) => { out[s.cat] = (out[s.cat] || 0) + 1; });
  // VELOSOFIZE imported layer (loaded async into window.VR_KMZ)
  const K = window.VR_KMZ;
  if (K) {
    K.routes.forEach((r) => { out[r.cat] = (out[r.cat] || 0) + 1; });
    K.points.forEach((p) => { out[p.cat] = (out[p.cat] || 0) + 1; });
  }
  return out;
}

function CommunityPanel({ theme, filter, onToggleCat, onSetAll, onClose, kmz = null }) {
  const { IconLegend, IconClose } = window;
  const cats = window.VR_COMMUNITY.cats;
  // recompute when the async VELOSOFIZE layer arrives so its counts appear
  const counts = React.useMemo(commCounts, [kmz]);
  const total = Object.values(counts).reduce((a, b) => a + b, 0);
  const shown = Object.keys(cats).filter((k) => !filter || filter[k] !== false)
    .reduce((a, k) => a + (counts[k] || 0), 0);
  const allOn = Object.keys(cats).every((k) => !filter || filter[k] !== false);

  return (
    <div style={{
      position: 'absolute', left: 14, top: 138, zIndex: 26, width: 214, pointerEvents: 'auto',
      borderRadius: 18, boxShadow: 'var(--shadow-lg)', overflow: 'hidden',
      ...glassStyle(theme, true),
    }}>
      {/* header */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 9, padding: '11px 11px 11px 13px', color: 'var(--text)' }}>
        <span style={{ width: 26, height: 26, borderRadius: 8, flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'var(--brand)', color: '#fff' }}><IconLegend size={16} /></span>
        <span style={{ flex: 1, minWidth: 0 }}>
          <span style={{ display: 'block', fontSize: 13.5, fontWeight: 800, lineHeight: 1.1 }}>Легенда</span>
          <span style={{ display: 'block', fontSize: 11, fontWeight: 600, color: 'var(--muted)', marginTop: 1 }}>{shown} от {total} маркера</span>
        </span>
        <button onClick={onClose} className="tap" style={{
          width: 26, height: 26, borderRadius: 26, border: 'none', cursor: 'pointer', flexShrink: 0,
          background: 'var(--surface-2)', color: 'var(--muted)',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}><IconClose size={15} /></button>
      </div>

      {/* select / deselect all */}
      <button onClick={() => onSetAll(!allOn)} className="tap" style={{
        width: '100%', background: 'none', border: 'none', cursor: 'pointer',
        display: 'flex', alignItems: 'center', gap: 8, padding: '8px 13px',
        borderTop: '0.5px solid var(--hairline)', borderBottom: '0.5px solid var(--hairline)',
      }}>
        <span style={{
          width: 19, height: 19, borderRadius: 6, flexShrink: 0,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          background: allOn ? 'var(--brand)' : 'transparent',
          border: allOn ? 'none' : '1.6px solid var(--faint)', color: '#fff',
        }}>{allOn && <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="3.2" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12.5l4.5 4.5L19 6"/></svg>}</span>
        <span style={{ flex: 1, textAlign: 'left', fontSize: 12.5, fontWeight: 700, color: 'var(--text)' }}>
          {allOn ? 'Изчисти всички' : 'Избери всички'}
        </span>
      </button>

      <div className="noscroll" style={{ maxHeight: 300, overflowY: 'auto', padding: '0 0 6px' }}>
        {CAT_GROUPS.map(([gname, keys]) => (
          <div key={gname}>
            <div style={{ fontSize: 10, fontWeight: 800, letterSpacing: 0.5, textTransform: 'uppercase', color: 'var(--faint)', padding: '8px 13px 3px' }}>{gname}</div>
            {keys.map((k) => {
              const active = !filter || filter[k] !== false;
              return (
                <button key={k} onClick={() => onToggleCat(k)} className="tap" style={{
                  width: '100%', background: 'none', border: 'none', cursor: 'pointer', textAlign: 'left',
                  display: 'flex', alignItems: 'center', gap: 10, padding: '6px 13px',
                  opacity: active ? 1 : 0.4, transition: 'opacity .15s',
                }}>
                  <CatChip cat={k} size={26} radius={8} />
                  <span style={{ flex: 1, fontSize: 13, fontWeight: 600, color: 'var(--text)' }}>{cats[k].label}</span>
                  <span style={{ fontSize: 12, fontWeight: 700, color: 'var(--faint)' }}>{counts[k]}</span>
                  <span style={{
                    width: 19, height: 19, borderRadius: 6, flexShrink: 0,
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                    background: active ? 'var(--brand)' : 'transparent',
                    border: active ? 'none' : '1.6px solid var(--faint)',
                    color: '#fff',
                  }}>{active && <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="3.2" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12.5l4.5 4.5L19 6"/></svg>}</span>
                </button>
              );
            })}
          </div>
        ))}
      </div>
    </div>
  );
}

// VELOSOFIZE imported features often have empty names + thin metadata (esp. bike-parking),
// so the sheet uses a friendly singular title + a default body per category instead of the
// bare category label.
const KMZ_TITLE = {
  parking: 'Велопаркинг', water: 'Чешма', crossing: 'Място за пресичане', transit: 'Спирка',
  bikelane: 'Велоалея', bikelane_shared: 'Велоалея', comfort: 'Удобна улица', rough: 'Черен път',
  sidewalk: 'Широк тротоар', untested: 'Веломаршрут', poi: 'Място за спорт',
};
const KMZ_BODY = {
  parking: 'Обществен велопаркинг от мрежата на VELOSOFIZE.',
  water: 'Чешма с питейна вода по маршрута.',
  crossing: 'Безопасно място за пресичане за велосипедисти.',
  transit: 'Спирка на обществения транспорт.',
  bikelane: 'Част от велоалейната мрежа на VELOSOFIZE.',
  bikelane_shared: 'Неотделена велоалея от мрежата на VELOSOFIZE.',
  comfort: 'Удобна за колоездене улица.',
  rough: 'Неасфалтиран участък — карайте внимателно.',
  sidewalk: 'Широк тротоар, подходящ за колоездене.',
  untested: 'Веломаршрут от мрежата на VELOSOFIZE.',
  poi: 'Място за спорт на открито.',
};

// VELOSOFIZE imported-feature metadata → Bulgarian labels/values for the detail sheet
const KMZ_META_LABEL = { bicycle_parking: 'Тип', capacity: 'Места', covered: 'Покрито', access: 'Достъп', Category: 'Линия' };
const KMZ_META_VAL = { stands: 'стойки', wall_loops: 'халки', rack: 'стойка', wave: 'вълна', bollard: 'колче', stand: 'стойка', shed: 'навес', building: 'сграда', informal: 'неформално', yes: 'да', no: 'не' };

function kmzMetaRows(meta) {
  if (!meta) return [];
  return Object.keys(meta).filter((k) => k !== 'amenity' && k !== '@id').map((k) => {
    let v = meta[k];
    if (k === 'capacity') { const n = parseFloat(v); v = isNaN(n) ? v : String(Math.round(n)); }
    else v = KMZ_META_VAL[v] || v;
    return { k: KMZ_META_LABEL[k] || k, v };
  });
}

function CommunityDetailSheet({ theme, item, onClose, go, onDirections }) {
  const { IconClose, IconPin, IconCheck, IconWarn, IconArrowUpRight, IconRoute, IconChevR } = window;
  const cats = window.VR_COMMUNITY.cats;
  const cat = cats[item.cat] || {};
  const [voted, setVoted] = React.useState(false);
  const [confirmed, setConfirmed] = React.useState(false);
  // "Потвърди" is the persisted action that can flip a report to active; "полезно" is a soft,
  // local-only signal — only confirm affects the displayed count, so the two never double-count.
  const votes = (item.votes || 0) + (confirmed ? 1 : 0);
  // a confirmation on a LIVE report (item.reportId) writes to Supabase; 3 confirms → active → routing.
  const liveItem = window.VR_DB && window.VR_DB.enabled && item.reportId;
  const doConfirm = async () => {
    if (confirmed) return;
    if (!liveItem) { setConfirmed(true); return; }     // seed item → local toggle (design demo)
    const user = await window.VR_DB.currentUser();
    if (!user) { go('auth'); return; }                  // must be signed in to confirm
    setConfirmed(true);                                 // optimistic
    const res = await window.VR_DB.confirmReport(item.reportId, 1);
    if (!res.error && window.vrHydrateCommunity) window.vrHydrateCommunity();
  };
  const comments = item.comments || [];
  const pending = item.status === 'pending';
  const official = item.by === 'Veloroute';
  // VELOSOFIZE imported features reuse this sheet but show KMZ metadata instead of
  // the crowdsourced voting/comments flow.
  const imported = !!item.imported;
  const title = item.label || item.name || (imported && KMZ_TITLE[item.cat]) || cat.label || '';
  const metaRows = imported ? kmzMetaRows(item.meta) : [];
  const link = imported ? (item.url || (item.meta && item.meta['@id'] ? 'https://www.openstreetmap.org/' + item.meta['@id'] : '')) : '';
  const isAmenity = cat.group === 'amenity' || cat.group === 'velopark';

  return (
    <React.Fragment>
      <div onClick={onClose} style={{ position: 'absolute', inset: 0, zIndex: 33, background: 'transparent', pointerEvents: 'auto' }} />
      <div style={{
        position: 'absolute', left: 0, right: 0, bottom: 0, zIndex: 34, pointerEvents: 'auto',
        maxHeight: '66%', display: 'flex', flexDirection: 'column',
        borderRadius: '26px 26px 0 0', boxShadow: 'var(--shadow-lg)',
        background: 'var(--surface)', animation: 'vr-sheet-up .34s cubic-bezier(.32,.72,0,1)',
        paddingBottom: 26,
      }}>
        {/* grabber */}
        <div style={{ display: 'flex', justifyContent: 'center', paddingTop: 9, flexShrink: 0 }}>
          <div style={{ width: 38, height: 4.5, borderRadius: 3, background: 'var(--hairline)' }} />
        </div>

        {/* header */}
        <div style={{ display: 'flex', alignItems: 'flex-start', gap: 12, padding: '10px 18px 4px', flexShrink: 0 }}>
          <CatChip cat={item.cat} size={46} radius={14} />
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 18, fontWeight: 800, lineHeight: 1.15, letterSpacing: -0.2 }}>{title}</div>
            {(item.area || imported) && (
              <div style={{ display: 'flex', alignItems: 'center', gap: 5, marginTop: 4, color: 'var(--muted)' }}>
                <IconPin size={14} />
                <span style={{ fontSize: 13, fontWeight: 600, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{item.area || cat.label}</span>
              </div>
            )}
          </div>
          <button onClick={onClose} className="tap" style={{ width: 32, height: 32, borderRadius: 32, border: 'none', cursor: 'pointer', background: 'var(--surface-2)', color: 'var(--muted)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
            <IconClose size={18} />
          </button>
        </div>

        {/* status pills */}
        <div style={{ display: 'flex', gap: 7, padding: '8px 18px 2px', flexWrap: 'wrap', flexShrink: 0 }}>
          {imported ? (
            <Pill color="navline" Icon={IconCheck}>VELOSOFIZE</Pill>
          ) : pending ? (
            <Pill color="moderate" Icon={IconWarn}>Непотвърдено</Pill>
          ) : official ? (
            <Pill color="navline" Icon={IconCheck}>Официално</Pill>
          ) : (
            <Pill color="safe" Icon={IconCheck}>Потвърдено от общността</Pill>
          )}
          <Pill color={cat.color}>{cat.label}</Pill>
        </div>

        <div className="noscroll" style={{ flex: 1, overflowY: 'auto', padding: '12px 18px 8px' }}>
          {item.desc && <p style={{ margin: 0, fontSize: 14.5, lineHeight: 1.5, color: 'var(--text)', fontWeight: 500, textWrap: 'pretty' }}>{item.desc}</p>}

          {imported ? (
            <React.Fragment>
              {metaRows.length > 0 && (
                <div style={{ marginTop: item.desc ? 12 : 2, display: 'flex', flexDirection: 'column', gap: 1, borderRadius: 13, overflow: 'hidden', border: '1px solid var(--hairline)' }}>
                  {metaRows.map((row, i) => (
                    <div key={i} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, padding: '10px 13px', background: 'var(--surface-2)', borderTop: i ? '1px solid var(--hairline)' : 'none' }}>
                      <span style={{ fontSize: 13, color: 'var(--muted)', fontWeight: 600 }}>{row.k}</span>
                      <span style={{ fontSize: 13.5, color: 'var(--text)', fontWeight: 700 }}>{row.v}</span>
                    </div>
                  ))}
                </div>
              )}
              {metaRows.length === 0 && !item.desc && (
                <div style={{ fontSize: 13.5, color: 'var(--muted)', fontWeight: 500, lineHeight: 1.5 }}>{KMZ_BODY[item.cat] || 'Част от мрежата на VELOSOFIZE.'}</div>
              )}
              {link && (
                <a href={link} target="_blank" rel="noopener" className="tap" style={{
                  display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 7, marginTop: 12, height: 44,
                  borderRadius: 13, textDecoration: 'none', border: '1.5px solid var(--hairline)',
                  color: 'var(--brand)', fontWeight: 700, fontSize: 14,
                }}>Виж повече <IconArrowUpRight size={16} /></a>
              )}
            </React.Fragment>
          ) : (
          <React.Fragment>
          {item.by && (
            <div style={{ fontSize: 12.5, color: 'var(--faint)', fontWeight: 600, marginTop: 9 }}>
              Докладвано от <span style={{ color: 'var(--muted)' }}>{item.by}</span>{item.ago ? ` · ${item.ago}` : ''}
            </div>
          )}

          {/* vote / confirm row */}
          <div style={{ display: 'flex', gap: 9, marginTop: 14 }}>
            <button onClick={() => setVoted((v) => !v)} className="tap" style={{
              flex: 1, height: 46, borderRadius: 13, cursor: 'pointer',
              border: voted ? 'none' : '1.5px solid var(--hairline)',
              background: voted ? 'color-mix(in oklch, var(--safe) 16%, var(--surface))' : 'var(--surface)',
              color: voted ? 'var(--safe)' : 'var(--text)', fontWeight: 700, fontSize: 14,
              display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 7,
            }}>
              <svg width="19" height="19" viewBox="0 0 24 24" fill={voted ? 'currentColor' : 'none'} stroke="currentColor" strokeWidth="1.9" strokeLinejoin="round"><path d="M12 5l7 8h-4v6H9v-6H5l7-8z"/></svg>
              {votes} полезно
            </button>
            {!isAmenity && (
              <button onClick={doConfirm} className="tap" style={{
                flex: 1, height: 46, borderRadius: 13, cursor: 'pointer',
                border: confirmed ? 'none' : '1.5px solid var(--hairline)',
                background: confirmed ? 'var(--brand)' : 'var(--surface)',
                color: confirmed ? '#fff' : 'var(--text)', fontWeight: 700, fontSize: 14,
                display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 7,
              }}>
                <IconCheck size={18} /> {confirmed ? 'Потвърдено' : 'Потвърди'}
              </button>
            )}
          </div>

          {/* comments */}
          <div style={{ marginTop: 18, fontSize: 13, fontWeight: 800, color: 'var(--text)' }}>
            Коментари {comments.length > 0 && <span style={{ color: 'var(--faint)' }}>· {comments.length}</span>}
          </div>
          {comments.length === 0 ? (
            <div style={{ fontSize: 13, color: 'var(--muted)', fontWeight: 500, marginTop: 7 }}>Бъди първият, който коментира.</div>
          ) : (
            <div style={{ marginTop: 8, display: 'flex', flexDirection: 'column', gap: 12 }}>
              {comments.map((c, i) => (
                <div key={i} style={{ display: 'flex', gap: 10 }}>
                  <span style={{ width: 30, height: 30, borderRadius: 30, flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 13, fontWeight: 800, color: '#fff', background: 'linear-gradient(140deg, var(--brand), color-mix(in oklch, var(--brand) 55%, #000))' }}>{c.by[0]}</span>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ display: 'flex', alignItems: 'baseline', gap: 7 }}>
                      <span style={{ fontSize: 13.5, fontWeight: 700 }}>{c.by}</span>
                      <span style={{ fontSize: 11.5, color: 'var(--faint)', fontWeight: 600 }}>{c.ago}</span>
                    </div>
                    <div style={{ fontSize: 13.5, color: 'var(--muted)', fontWeight: 500, lineHeight: 1.4, marginTop: 1 }}>{c.text}</div>
                  </div>
                </div>
              ))}
            </div>
          )}

          {/* comment input (decorative) */}
          <div style={{ display: 'flex', alignItems: 'center', gap: 9, marginTop: 14, height: 42, borderRadius: 12, background: 'var(--surface-2)', padding: '0 6px 0 14px' }}>
            <span style={{ flex: 1, fontSize: 13.5, color: 'var(--faint)', fontWeight: 500 }}>Добави коментар…</span>
            <span style={{ width: 30, height: 30, borderRadius: 30, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'var(--brand)', color: '#fff' }}><IconArrowUpRight size={17} /></span>
          </div>
          </React.Fragment>
          )}
        </div>

        {/* primary action */}
        <div style={{ display: 'flex', gap: 9, padding: '8px 18px 0', flexShrink: 0 }}>
          <button onClick={() => {
            // route to THIS feature (point → its coord; line → its centre), not the last search
            const target = item.at ? item.at
              : (item.coords && item.coords.length ? (() => { const c = L.latLngBounds(item.coords).getCenter(); return [c.lat, c.lng]; })() : null);
            if (target && onDirections) onDirections(target, item.label || item.name || 'Маркер', item.area || item.sub || '');
            else go('route');
          }} className="tap" style={{
            flex: 1, height: 50, borderRadius: 15, border: 'none', cursor: 'pointer',
            background: 'var(--brand)', color: '#fff', fontWeight: 800, fontSize: 15.5,
            display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
            boxShadow: '0 6px 18px color-mix(in oklch, var(--brand) 40%, transparent)',
          }}><IconRoute size={20} /> {isAmenity ? 'Маршрут до тук' : (imported ? 'Маршрут по тази отсечка' : 'Заобиколи / маршрут')}</button>
        </div>
      </div>
    </React.Fragment>
  );
}

function Pill({ color, Icon, children }) {
  const c = `var(--${color})`;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 5, padding: '4px 10px',
      borderRadius: 999, fontSize: 12, fontWeight: 700, color: c,
      background: `color-mix(in oklch, ${c} 15%, transparent)`,
    }}>
      {Icon && <Icon size={13} sw={2.2} />}{children}
    </span>
  );
}

Object.assign(window, { CommunityPanel, CommunityDetailSheet, CatChip, Pill });
