// ============================================================
// REVIEWER FEEDBACK, Corporate demo
// The single end-of-demo "Give Feedback" panel: a full ranking board
// (rate each feature, drag into priority tiers) + live summary + submit
// (Google-Sheets-ready payload). Opens on the `open-feedback` event.
// Reads/writes the shared store; state persists to localStorage.
// Not part of the shipping product.
// ============================================================
(function () {
  const { useState, useEffect, useRef } = React;

  const F = { orange: '#f28020', orangeDeep: '#b35f18', orange100: '#fdf0e4', orange50: '#fef9f4', navy: '#002857', star: '#d6b84d' };
  const LS_KEY = 'la_feedback_v1';

  // ---- Features (seeded from real app screens). page = "Explore again" target ----
  const FEATURES = [
    { id: 'home',    name: 'Home dashboard & LocalScore',   desc: 'The daily at-a-glance: LocalScore health ring, KPIs, and Lenny’s top moves.', page: 'home' },
    { id: 'ent-rep', name: 'Enterprise Reporting',           desc: 'Cross-brand rollups, exportable scorecards, and board-ready summaries.', page: 'enterprise-reporting' },
    { id: 'playbook',name: 'Network Playbook',               desc: 'The decision system governing strategy across every location.', page: 'services' },
    { id: 'budget',  name: 'Budget (local view)',           desc: 'The local operator’s budget tab, one smart budget or per-channel control, with pacing and spend-vs-performance.', page: 'budget' },
    { id: 'results', name: 'Results & channel performance',  desc: 'Spend, leads, and ROAS broken out by channel with trend lines.', page: 'results' },
    { id: 'bench',   name: 'Benchmarks',                     desc: 'How each location stacks up against the network and its peer set.', page: 'bench' },
    { id: 'reviews', name: 'Reviews & reputation',           desc: 'Ratings by platform, response tracking, and reputation coaching.', page: 'reviews' },
    { id: 'listings',name: 'Listings management',            desc: 'Directory accuracy across 40+ listings with fix-it prompts.', page: 'listings' },
    { id: 'leads',   name: 'Leads inbox',                    desc: 'Every call, form, and message captured and attributed to a channel.', page: 'leads' },
    { id: 'users',   name: 'Local users & Mirror mode',      desc: 'Browse franchise accounts and step into any operator’s exact view.', page: 'local-users' },
    { id: 'hq',      name: 'Corporate HQ rollup',            desc: 'Portfolio-wide performance by region, tier, and initiative.', page: 'hq' },
    { id: 'lenny',   name: 'Lenny, AI marketing coach',     desc: 'Conversational assistant that flags wins, fires, and next moves.', event: 'open-lenny' },
    { id: 'invoice', name: 'Billing & invoices',             desc: 'Statements, line-item spend, and self-serve billing history.', page: 'invoice' },
    { id: 'settings',   name: 'Corporate settings',            desc: 'Brand-level configuration: conversion values, defaults, and governance.', page: 'settings' },
    { id: 'stack',      name: 'Marketing Stack',               desc: 'Every service across the network, who’s enrolled and what corporate funds.', page: 'services' },
    { id: 'support',    name: 'Franchisee Support',            desc: 'Franchisee communications and advisor performance.', page: 'support' },
    { id: 'density',    name: 'Owner view vs Pro view',        desc: 'Two densities for local users, a calm owner summary or full pro detail.', page: 'home' },
    { id: 'initiatives',name: 'Initiatives (local view)',      desc: 'The local operator’s marketing initiatives, enrolled, funded, recommended.', page: 'services' },
  ];

  const TAGS = ['Clear', 'Saves time', 'Must-have', 'Confusing', 'Needs work', 'Delightful'];
  const TIERS = [
    { key: 'pool', label: 'To review',      hint: 'Rate, then drag into a bucket', color: 'var(--ink-4)', bg: 'var(--cream)' },
    { key: 'must', label: 'Must-have',      hint: 'Build this first',              color: F.orange,        bg: F.orange50 },
    { key: 'nice', label: 'Nice-to-have',   hint: 'Valuable, not urgent',          color: 'var(--sage)',   bg: 'var(--sage-soft)' },
    { key: 'skip', label: 'Skip / not now', hint: 'Low priority for us',           color: 'var(--ink-4)',  bg: 'var(--cream-2)' },
  ];

  // ============================================================
  // Shared store (framework-agnostic pub/sub, localStorage-backed)
  // ============================================================
  function defaultState() {
    const items = {};
    FEATURES.forEach(f => { items[f.id] = { rating: 0, tags: [], comment: '' }; });
    return { order: { pool: FEATURES.map(f => f.id), must: [], nice: [], skip: [] }, items, custom: [], general: '', submitted: false };
  }
  function loadState() {
    try {
      const raw = localStorage.getItem(LS_KEY);
      if (raw) {
        const st = JSON.parse(raw);
        // Migration: seed any newly-added features into an existing saved state
        FEATURES.forEach(f => {
          if (!st.items[f.id]) {
            st.items[f.id] = { rating: 0, tags: [], comment: '' };
            st.order.pool.push(f.id);
          }
        });
        // Prune any retired features (removed from FEATURES) from saved state
        const valid = new Set([...FEATURES.map(f => f.id), ...((st.custom || []).map(c => c.id))]);
        Object.keys(st.items).forEach(id => { if (!valid.has(id)) delete st.items[id]; });
        ['pool', 'must', 'nice', 'skip'].forEach(k => {
          if (st.order && Array.isArray(st.order[k])) st.order[k] = st.order[k].filter(id => valid.has(id));
        });
        if (typeof st.general !== 'string') st.general = '';
        return st;
      }
    } catch (e) {}
    return defaultState();
  }
  const store = {
    state: loadState(),
    subs: [],
    get() { return this.state; },
    set(updater) {
      this.state = typeof updater === 'function' ? updater(this.state) : updater;
      try { localStorage.setItem(LS_KEY, JSON.stringify(this.state)); } catch (e) {}
      this.subs.forEach(fn => fn(this.state));
    },
    subscribe(fn) { this.subs.push(fn); return () => { this.subs = this.subs.filter(f => f !== fn); }; },
  };
  window.FeedbackStore = store;

  function useFeedback() {
    const [, force] = useState(0);
    useEffect(() => store.subscribe(() => force(x => x + 1)), []);
    return store.get();
  }
  const featureById = (id, custom) => FEATURES.find(f => f.id === id) || (custom || []).find(f => f.id === id);

  // ---- mutations ----
  const M = {
    rate: (id, v) => store.set(b => ({ ...b, items: { ...b.items, [id]: { ...b.items[id], rating: v } } })),
    toggleTag: (id, t) => store.set(b => {
      const cur = b.items[id].tags; const tags = cur.includes(t) ? cur.filter(x => x !== t) : [...cur, t];
      return { ...b, items: { ...b.items, [id]: { ...b.items[id], tags } } };
    }),
    comment: (id, c) => store.set(b => ({ ...b, items: { ...b.items, [id]: { ...b.items[id], comment: c } } })),
    setGeneral: (g) => store.set(b => ({ ...b, general: g })),
    add: (name) => {
      const id = 'custom-' + Date.now();
      store.set(b => ({ ...b, custom: [...b.custom, { id, name, desc: 'Added by you', custom: true }],
        items: { ...b.items, [id]: { rating: 0, tags: [], comment: '' } },
        order: { ...b.order, pool: [...b.order.pool, id] } }));
    },
    removeCustom: (id) => store.set(b => {
      const order = {}; Object.keys(b.order).forEach(k => order[k] = b.order[k].filter(x => x !== id));
      const items = { ...b.items }; delete items[id];
      return { ...b, order, items, custom: b.custom.filter(f => f.id !== id) };
    }),
    move: (id, toCol, toIndex) => store.set(b => {
      const from = Object.keys(b.order).find(k => b.order[k].includes(id));
      if (!from) return b;
      const order = {}; Object.keys(b.order).forEach(k => order[k] = [...b.order[k]]);
      order[from].splice(order[from].indexOf(id), 1);
      let idx = (toIndex == null || toIndex > order[toCol].length) ? order[toCol].length : toIndex;
      order[toCol].splice(idx, 0, id);
      return { ...b, order };
    }),
    reset: () => { localStorage.removeItem(LS_KEY); store.set(defaultState()); },
  };
  function buildPayload() {
    const b = store.get(); const rows = [];
    Object.keys(b.order).forEach(tierKey => b.order[tierKey].forEach((id, i) => {
      const f = featureById(id, b.custom); const it = b.items[id];
      rows.push({ featureId: id, feature: f ? f.name : id, tier: tierKey, rankInTier: tierKey === 'pool' ? null : i + 1,
        rating: it.rating, tags: it.tags.join('; '), comment: it.comment, custom: !!(f && f.custom) });
    }));
    return { submittedAt: new Date().toISOString(), program: 'LOCALACT Redesign, Corporate Reviewer', general: b.general || '', rows };
  }

  // ============================================================
  // Stars
  // ============================================================
  function Stars({ value, onChange, size = 20 }) {
    const [hover, setHover] = useState(0);
    return (
      <div style={{ display: 'inline-flex', gap: 3 }} onMouseLeave={() => setHover(0)}>
        {[1, 2, 3, 4, 5].map(n => {
          const on = (hover || value) >= n;
          return (
            <svg key={n} width={size} height={size} viewBox="0 0 24 24"
              onMouseEnter={() => setHover(n)}
              onClick={(e) => { e.stopPropagation(); onChange(n === value ? 0 : n); }}
              style={{ cursor: 'pointer', color: on ? F.star : 'var(--cream-3)', transition: 'color .1s, transform .1s', transform: hover === n ? 'scale(1.15)' : 'none' }}
              fill="currentColor">
              <path d="M12 2l2.9 6.3 6.9.7-5.2 4.6 1.5 6.8L12 17.6 5.9 20.4l1.5-6.8L2.2 9l6.9-.7z" />
            </svg>
          );
        })}
      </div>
    );
  }

  // Tag + comment editor shared by dock and card
  function DetailEditor({ state, id }) {
    return (
      <div onClick={e => e.stopPropagation()}>
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginBottom: 9 }}>
          {TAGS.map(t => {
            const sel = state.tags.includes(t);
            return (
              <span key={t} onClick={() => M.toggleTag(id, t)}
                style={{ fontSize: 11, padding: '3px 9px', borderRadius: 999, cursor: 'pointer', userSelect: 'none',
                  border: sel ? `1px solid ${F.orange}` : '1px solid var(--line)', background: sel ? F.orange : 'var(--paper)', color: sel ? '#fff' : 'var(--ink-2)', fontWeight: sel ? 600 : 500 }}>
                {t}
              </span>
            );
          })}
        </div>
        <textarea value={state.comment} onChange={(e) => M.comment(id, e.target.value)} rows={2}
          placeholder="What would you change? (optional)"
          style={{ width: '100%', border: '1px solid var(--line)', background: 'var(--cream)', borderRadius: 10, padding: '8px 10px',
            fontFamily: 'var(--ff-ui)', fontSize: 12, resize: 'vertical', color: 'var(--ink)', boxSizing: 'border-box' }} />
      </div>
    );
  }

  // ============================================================
  // Feature card (ranking board)
  // ============================================================
  function FeatureCard({ feature, state, tier, index, onExplore, dragging, dropHint,
                         onDragStart, onDragEnd, onDragOverCard }) {
    const [open, setOpen] = useState(false);
    return (
      <div draggable
        onDragStart={(e) => { e.dataTransfer.effectAllowed = 'move'; onDragStart(feature.id); }}
        onDragEnd={onDragEnd}
        onDragOver={(e) => { e.preventDefault(); onDragOverCard(tier, index); }}
        style={{ background: 'var(--paper)', border: dropHint ? undefined : '1px solid var(--line)', borderRadius: 12, padding: '12px 13px',
          boxShadow: dragging ? 'none' : 'var(--sh-1)', opacity: dragging ? 0.4 : 1, cursor: 'grab', position: 'relative',
          borderTop: dropHint ? `2px solid ${F.orange}` : '1px solid var(--line)', transition: 'box-shadow .15s' }}>
        <div style={{ display: 'flex', alignItems: 'flex-start', gap: 9 }}>
          <span style={{ color: 'var(--cream-3)', fontSize: 15, lineHeight: 1.2, letterSpacing: '-2px', marginTop: 1, flexShrink: 0 }}>⠿</span>
          {tier !== 'pool' && (
            <span style={{ flexShrink: 0, width: 20, height: 20, borderRadius: 6, background: F.navy, color: '#fff',
              fontFamily: 'var(--ff-display)', fontSize: 11, fontWeight: 600, display: 'flex', alignItems: 'center', justifyContent: 'center', marginTop: 1 }}>{index + 1}</span>
          )}
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
              <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--ink)', lineHeight: 1.25 }}>{feature.name}</div>
              {feature.custom && <span style={{ fontSize: 8.5, fontWeight: 700, letterSpacing: '.06em', color: F.orangeDeep, background: F.orange100, padding: '1px 5px', borderRadius: 4 }}>YOURS</span>}
            </div>
            <div style={{ fontSize: 11, color: 'var(--ink-3)', lineHeight: 1.4, marginTop: 2 }}>{feature.desc}</div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginTop: 8, flexWrap: 'wrap' }}>
              <Stars value={state.rating} onChange={(v) => M.rate(feature.id, v)} size={18} />
              <button onClick={(e) => { e.stopPropagation(); setOpen(o => !o); }}
                style={{ background: 'none', border: 'none', padding: 0, cursor: 'pointer', fontSize: 11, fontWeight: 600,
                  color: (state.tags.length || state.comment) ? F.orangeDeep : 'var(--ink-4)', fontFamily: 'var(--ff-ui)' }}>
                {state.tags.length || state.comment ? `${state.tags.length} tag${state.tags.length !== 1 ? 's' : ''}${state.comment ? ' · note' : ''}` : '+ Add detail'}
              </button>
              <div style={{ flex: 1 }} />
              {feature.custom
                ? <button onClick={(e) => { e.stopPropagation(); M.removeCustom(feature.id); }} style={{ background: 'none', border: 'none', padding: 0, cursor: 'pointer', fontSize: 11, fontWeight: 600, color: 'var(--bad)', fontFamily: 'var(--ff-ui)' }}>Remove</button>
                : (feature.page || feature.event) && <button onClick={(e) => { e.stopPropagation(); onExplore(feature); }} style={{ background: 'none', border: 'none', padding: 0, cursor: 'pointer', fontSize: 11, fontWeight: 600, color: 'var(--terra)', fontFamily: 'var(--ff-ui)', whiteSpace: 'nowrap' }}>↩ Explore again</button>}
            </div>
            {open && <div style={{ marginTop: 10, paddingTop: 10, borderTop: '1px dashed var(--line)' }}><DetailEditor state={state} id={feature.id} /></div>}
          </div>
        </div>
      </div>
    );
  }

  function Column({ tier, ids, board, dragState, dnd, onExplore }) {
    return (
      <div onDragOver={(e) => { e.preventDefault(); dnd.onDragOverCol(tier.key); }} onDrop={(e) => { e.preventDefault(); dnd.onDrop(); }} style={{ display: 'flex', flexDirection: 'column', minWidth: 0 }}>
        <div style={{ padding: '2px 4px 10px' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            <span style={{ width: 9, height: 9, borderRadius: '50%', background: tier.color, flexShrink: 0 }} />
            <div style={{ fontFamily: 'var(--ff-display)', fontSize: 14, fontWeight: 600, color: 'var(--ink)', whiteSpace: 'nowrap' }}>{tier.label}</div>
            <span style={{ fontSize: 11, fontWeight: 600, color: 'var(--ink-4)', background: 'var(--cream-2)', borderRadius: 999, padding: '1px 8px' }}>{ids.length}</span>
          </div>
          <div style={{ fontSize: 10.5, color: 'var(--ink-4)', marginTop: 3, paddingLeft: 17 }}>{tier.hint}</div>
        </div>
        <div style={{ flex: 1, background: tier.bg, borderRadius: 14, padding: 10, display: 'flex', flexDirection: 'column', gap: 8,
          border: dragState.overCol === tier.key ? `2px dashed ${F.orange}` : '2px dashed transparent', minHeight: 120, transition: 'border-color .12s' }}>
          {ids.map((id, i) => {
            const feature = featureById(id, board.custom); if (!feature) return null;
            return (
              <FeatureCard key={id} feature={feature} state={board.items[id]} tier={tier.key} index={i}
                dragging={dragState.dragId === id} dropHint={dragState.overCol === tier.key && dragState.overIndex === i && dragState.dragId !== id}
                onExplore={onExplore} onDragStart={dnd.onDragStart} onDragEnd={dnd.onDragEnd} onDragOverCard={dnd.onDragOverCard} />
            );
          })}
          {ids.length === 0 && <div style={{ fontSize: 11.5, color: 'var(--ink-5)', textAlign: 'center', padding: '18px 8px', fontStyle: 'italic' }}>Drag features here</div>}
        </div>
      </div>
    );
  }

  // General open-comment card with optional browser voice dictation.
  function GeneralFeedbackCard({ value }) {
    const [listening, setListening] = useState(false);
    const recRef = useRef(null);
    const baseRef = useRef('');
    const SR = (typeof window !== 'undefined') && (window.SpeechRecognition || window.webkitSpeechRecognition);
    useEffect(() => () => { try { recRef.current && recRef.current.stop(); } catch (e) {} }, []);
    const toggle = () => {
      if (!SR) return;
      if (listening) { try { recRef.current && recRef.current.stop(); } catch (e) {} return; }
      const rec = new SR();
      rec.continuous = true; rec.interimResults = true; rec.lang = 'en-US';
      baseRef.current = value ? value.replace(/\s+$/, '') + ' ' : '';
      rec.onresult = (e) => {
        let txt = '';
        for (let k = 0; k < e.results.length; k++) txt += e.results[k][0].transcript;
        M.setGeneral((baseRef.current + txt).replace(/\s+/g, ' ').trimStart());
      };
      rec.onend = () => setListening(false);
      rec.onerror = () => setListening(false);
      recRef.current = rec;
      try { rec.start(); setListening(true); } catch (e) { setListening(false); }
    };
    return (
      <div style={{ background: 'var(--paper)', border: '1px solid var(--line)', borderRadius: 14, padding: 16 }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8, marginBottom: 4 }}>
          <div style={{ fontFamily: 'var(--ff-display)', fontSize: 14, fontWeight: 600 }}>Anything else on your mind?</div>
          {SR && (
            <button onClick={toggle} title={listening ? 'Stop dictation' : 'Dictate with your voice'}
              style={{ display: 'flex', alignItems: 'center', gap: 5, border: `1px solid ${listening ? F.orange : 'var(--line)'}`, background: listening ? F.orange : 'var(--paper)', color: listening ? '#fff' : 'var(--ink-3)', borderRadius: 999, padding: '5px 11px', fontSize: 11.5, fontWeight: 600, cursor: 'pointer', fontFamily: 'var(--ff-ui)' }}>
              <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="9" y="2" width="6" height="12" rx="3" /><path d="M19 10a7 7 0 0 1-14 0M12 17v4" /></svg>
              {listening ? 'Listening…' : 'Dictate'}
            </button>
          )}
        </div>
        <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginBottom: 8 }}>Open feedback, optional. Type or speak your mind.</div>
        <textarea value={value} onChange={(e) => M.setGeneral(e.target.value)} rows={4}
          placeholder="What worked, what didn’t, what you’d want next…"
          style={{ width: '100%', boxSizing: 'border-box', border: '1px solid var(--line)', background: 'var(--cream)', borderRadius: 10, padding: '9px 11px', fontSize: 13, fontFamily: 'var(--ff-ui)', color: 'var(--ink)', lineHeight: 1.5, resize: 'vertical', outline: 'none' }} />
      </div>
    );
  }

  // ============================================================
  // Live summary (used inside overlay). Recomputes on every store change.
  // ============================================================
  function SummaryRail({ board, onSubmit }) {
    const rated = Object.values(board.items).filter(it => it.rating > 0).length;
    const total = FEATURES.length + board.custom.length;
    const avg = rated ? (Object.values(board.items).reduce((s, it) => s + it.rating, 0) / rated).toFixed(1) : '–';
    const counts = { must: board.order.must.length, nice: board.order.nice.length, skip: board.order.skip.length, pool: board.order.pool.length };
    const sorted = total - counts.pool;
    const Row = ({ label, value, color }) => (
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, padding: '7px 0' }}>
        <span style={{ fontSize: 12, color: 'var(--ink-3)', whiteSpace: 'nowrap' }}>{label}</span>
        <span style={{ fontFamily: 'var(--ff-display)', fontSize: 15, fontWeight: 600, color: color || 'var(--ink)' }}>{value}</span>
      </div>
    );
    return (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 12, position: 'sticky', top: 0 }}>
        <div style={{ background: 'var(--paper)', border: '1px solid var(--line)', borderRadius: 14, padding: 16 }}>
          <div style={{ fontFamily: 'var(--ff-display)', fontSize: 14, fontWeight: 600, marginBottom: 4 }}>Your response</div>
          <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginBottom: 6 }}>Updates live as you rate.</div>
          <div style={{ borderTop: '1px solid var(--line)' }}>
            <Row label="Features rated" value={`${rated}/${total}`} />
            <Row label="Average rating" value={avg !== '–' ? `${avg}★` : '–'} />
            <Row label="Sorted into tiers" value={`${sorted}/${total}`} />
          </div>
          <div style={{ borderTop: '1px solid var(--line)', marginTop: 4, paddingTop: 6 }}>
            <Row label="Must-have" value={counts.must} color={F.orange} />
            <Row label="Nice-to-have" value={counts.nice} color="var(--sage)" />
            <Row label="Skip / not now" value={counts.skip} color="var(--ink-4)" />
          </div>
        </div>
        <GeneralFeedbackCard value={board.general || ''} />
        <div style={{ background: F.orange50, border: `1px solid ${F.orange100}`, borderRadius: 14, padding: 16 }}>
          <div style={{ fontSize: 12, color: 'var(--ink-2)', lineHeight: 1.5, marginBottom: 12 }}>When you’re happy with the order, submit. Your ranking and every rating land in our reviewer database.</div>
          <button onClick={onSubmit} disabled={sorted === 0}
            style={{ width: '100%', border: 'none', borderRadius: 10, padding: '11px 16px', cursor: sorted === 0 ? 'not-allowed' : 'pointer',
              background: sorted === 0 ? 'var(--cream-3)' : F.orange, color: '#fff', fontFamily: 'var(--ff-display)', fontWeight: 600, fontSize: 14 }}>Submit feedback</button>
          <button onClick={() => { if (window.confirm('Clear all ratings and rankings?')) M.reset(); }}
            style={{ width: '100%', marginTop: 8, border: 'none', background: 'none', color: 'var(--ink-4)', fontSize: 11.5, fontWeight: 600, cursor: 'pointer', fontFamily: 'var(--ff-ui)' }}>Reset everything</button>
        </div>
      </div>
    );
  }

  function Submitted({ payload, onClose, onReopen }) {
    const [showJson, setShowJson] = useState(false);
    return (
      <div style={{ maxWidth: 640, margin: '40px auto', textAlign: 'center' }}>
        <div style={{ width: 64, height: 64, borderRadius: '50%', background: F.orange100, color: F.orange, display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 18px' }}>
          <svg width="30" height="30" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M20 6L9 17l-5-5" /></svg>
        </div>
        <div style={{ fontFamily: 'var(--ff-display)', fontSize: 26, fontWeight: 600, color: 'var(--ink)', letterSpacing: '-0.01em' }}>Thank you, feedback received</div>
        <div style={{ fontSize: 14, color: 'var(--ink-3)', marginTop: 8, lineHeight: 1.55 }}>Your {payload.rows.filter(r => r.tier !== 'pool').length} ranked features and {payload.rows.filter(r => r.rating > 0).length} ratings have been recorded. You can revisit and resubmit any time.</div>
        <div style={{ display: 'flex', gap: 10, justifyContent: 'center', marginTop: 22 }}>
          <button onClick={onReopen} style={{ border: `1px solid ${F.orange}`, background: '#fff', color: F.orangeDeep, borderRadius: 10, padding: '10px 18px', fontFamily: 'var(--ff-display)', fontWeight: 600, fontSize: 13, cursor: 'pointer' }}>Edit my ranking</button>
          <button onClick={onClose} style={{ border: 'none', background: F.orange, color: '#fff', borderRadius: 10, padding: '10px 18px', fontFamily: 'var(--ff-display)', fontWeight: 600, fontSize: 13, cursor: 'pointer' }}>Back to the demo</button>
        </div>
        <button onClick={() => setShowJson(s => !s)} style={{ marginTop: 26, background: 'none', border: 'none', color: 'var(--ink-4)', fontSize: 11.5, fontWeight: 600, cursor: 'pointer' }}>{showJson ? 'Hide' : 'View'} submission data (what writes to the sheet)</button>
        {showJson && <pre style={{ textAlign: 'left', background: F.navy, color: '#e5eefd', borderRadius: 12, padding: 16, fontSize: 11, lineHeight: 1.5, overflow: 'auto', maxHeight: 300, marginTop: 12 }}>{JSON.stringify(payload, null, 2)}</pre>}
      </div>
    );
  }

  function explore(feature) {
    window.dispatchEvent(new CustomEvent('close-feedback'));
    if (feature.event) window.dispatchEvent(new CustomEvent(feature.event));
    else if (feature.page) window.dispatchEvent(new CustomEvent('go-page', { detail: feature.page }));
    window.toast && window.toast('Exploring “' + feature.name + '”', 'Rate it in the panel on the left, or reopen the board anytime.', 'info');
  }

  // ============================================================
  // The full ranking overlay
  // ============================================================
  function Overlay({ open, onClose }) {
    const board = useFeedback();
    const [showThanks, setShowThanks] = useState(false);
    const [lastPayload, setLastPayload] = useState(null);
    const [addOpen, setAddOpen] = useState(false);
    const [addName, setAddName] = useState('');
    const [dragState, setDragState] = useState({ dragId: null, overCol: null, overIndex: null });
    const drag = useRef({ dragId: null, overCol: null, overIndex: null });
    const setDrag = (patch) => { drag.current = { ...drag.current, ...patch }; setDragState({ ...drag.current }); };

    const dnd = {
      onDragStart: (id) => setDrag({ dragId: id }),
      onDragOverCard: (col, index) => { if (drag.current.dragId) setDrag({ overCol: col, overIndex: index }); },
      onDragOverCol: (col) => { if (drag.current.dragId && drag.current.overCol !== col) setDrag({ overCol: col, overIndex: null }); },
      onDrop: () => { const { dragId, overCol, overIndex } = drag.current; if (dragId && overCol) M.move(dragId, overCol, overIndex); dnd.onDragEnd(); },
      onDragEnd: () => setDrag({ dragId: null, overCol: null, overIndex: null }),
    };
    const submit = () => { const p = buildPayload(); setLastPayload(p); store.set(b => ({ ...b, submitted: true })); setShowThanks(true); window.confettiBurst && window.confettiBurst(); window.__LA_LAST_FEEDBACK = p;
      // Send to the Google Apps Script backend. no-cors means we can't read the
      // response, so submissions are confirmed by checking the Sheet. The
      // localStorage write above stays as a local backup.
      try {
        fetch("https://script.google.com/macros/s/AKfycbzQ0oxvljzKp-UKx3xO6YRWCI8rSmUfAnnIKhKiu9supuTLwQv5Pu82LNPoLlwovznY/exec", {
          method: 'POST', mode: 'no-cors',
          headers: { 'Content-Type': 'text/plain;charset=utf-8' },
          body: JSON.stringify(Object.assign({}, p, {
            email: localStorage.getItem('demo_user_email') || 'anonymous'
          }))
        });
      } catch (e) {}
    };
    useEffect(() => { if (open) setShowThanks(false); }, [open]);
    if (!open) return null;

    const addCustom = () => { const n = addName.trim(); if (!n) return; M.add(n); setAddName(''); setAddOpen(false); };

    return (
      <ModalPortal>
        <div style={{ position: 'fixed', inset: 0, zIndex: 8500, background: 'var(--cream)', display: 'flex', flexDirection: 'column', animation: 'fb-in .22s ease' }}>
          <div style={{ background: F.navy, color: '#fff', flexShrink: 0 }}>
            <div style={{ height: 4, background: `repeating-linear-gradient(45deg, ${F.orange} 0 14px, ${F.orangeDeep} 14px 28px)` }} />
            <div style={{ display: 'flex', alignItems: 'center', gap: 16, padding: '14px 26px' }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <span style={{ fontFamily: 'var(--ff-display)', fontWeight: 700, letterSpacing: '.04em', fontSize: 16 }}>LOCAL<span style={{ color: F.orange }}>ACT</span></span>
                <span style={{ fontSize: 9, fontWeight: 700, letterSpacing: '.1em', background: F.orange, color: '#fff', padding: '3px 8px', borderRadius: 5 }}>DEMO FEEDBACK</span>
              </div>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontFamily: 'var(--ff-display)', fontSize: 15, fontWeight: 600 }}>Rate &amp; rank the redesign</div>
                <div style={{ fontSize: 11.5, color: 'rgba(255,255,255,.6)' }}>This is a preview feedback tool, not part of the live product. Nothing here affects real accounts.</div>
              </div>
              <button onClick={onClose} style={{ background: 'rgba(255,255,255,.1)', border: '1px solid rgba(255,255,255,.2)', color: '#fff', borderRadius: 9, padding: '8px 14px', cursor: 'pointer', fontFamily: 'var(--ff-ui)', fontWeight: 600, fontSize: 12.5 }}>Close &amp; keep exploring</button>
            </div>
          </div>
          <div style={{ flex: 1, overflow: 'auto' }}>
            {showThanks && lastPayload
              ? <Submitted payload={lastPayload} onClose={onClose} onReopen={() => setShowThanks(false)} />
              : (
                <div style={{ maxWidth: 1320, margin: '0 auto', padding: '22px 26px 60px' }}>
                  <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', gap: 16, marginBottom: 18, flexWrap: 'wrap' }}>
                    <div>
                      <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '.12em', textTransform: 'uppercase', color: F.orange, marginBottom: 4 }}>Prioritize</div>
                      <h2 style={{ fontFamily: 'var(--ff-display)', fontSize: 22, fontWeight: 600, color: 'var(--ink)', margin: 0, letterSpacing: '-0.01em' }}>Drag each feature into a priority bucket</h2>
                      <p style={{ fontSize: 13, color: 'var(--ink-3)', margin: '5px 0 0', maxWidth: 640 }}>Order inside <b>Must-have</b> and <b>Nice-to-have</b> is your priority, top = build first. Ratings you gave while exploring are already here; keep tweaking them anytime.</p>
                    </div>
                    <div>
                      {addOpen ? (
                        <div style={{ display: 'flex', gap: 6 }}>
                          <input autoFocus value={addName} onChange={e => setAddName(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') addCustom(); if (e.key === 'Escape') setAddOpen(false); }} placeholder="Name a feature…" style={{ border: '1px solid var(--line)', borderRadius: 9, padding: '8px 11px', fontSize: 13, fontFamily: 'var(--ff-ui)', width: 210 }} />
                          <button onClick={addCustom} style={{ border: 'none', background: F.orange, color: '#fff', borderRadius: 9, padding: '8px 14px', fontWeight: 600, fontSize: 12.5, cursor: 'pointer', fontFamily: 'var(--ff-display)' }}>Add</button>
                        </div>
                      ) : (
                        <button onClick={() => setAddOpen(true)} style={{ border: `1px dashed ${F.orange}`, background: F.orange50, color: F.orangeDeep, borderRadius: 9, padding: '9px 15px', fontWeight: 600, fontSize: 12.5, cursor: 'pointer', fontFamily: 'var(--ff-display)' }}>+ Add a feature we missed</button>
                      )}
                    </div>
                  </div>
                  <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0,1fr) 240px', gap: 22, alignItems: 'start' }}>
                    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0,1fr))', gap: 16 }}>
                      {TIERS.map(t => <Column key={t.key} tier={t} ids={board.order[t.key]} board={board} dragState={dragState} dnd={dnd} onExplore={explore} />)}
                    </div>
                    <SummaryRail board={board} onSubmit={submit} />
                  </div>
                </div>
              )}
          </div>
        </div>
        <style>{`@keyframes fb-in { from { opacity: 0; } to { opacity: 1; } }`}</style>
      </ModalPortal>
    );
  }

  // ============================================================
  // Root
  // ============================================================
  function FeedbackExperience({ page }) {
    const [open, setOpen] = useState(false);
    useEffect(() => {
      const openFn = () => setOpen(true), closeFn = () => setOpen(false);
      window.addEventListener('open-feedback', openFn);
      window.addEventListener('close-feedback', closeFn);
      return () => { window.removeEventListener('open-feedback', openFn); window.removeEventListener('close-feedback', closeFn); };
    }, []);
    return (
      <React.Fragment>
        <Overlay open={open} onClose={() => setOpen(false)} />
      </React.Fragment>
    );
  }
  window.FeedbackExperience = FeedbackExperience;

  // Shared API for the guided review (guided-review.jsx), same store, same mutations.
  window.FeedbackAPI = { store, M, FEATURES, featureById, useFeedback, Stars, DetailEditor, TAGS };
})();
