/* OnSumn — FlyerUpload modal (Phase 4)
   Single shell that morphs through 4 states:
   Empty → Loading → Review → Success
   Pattern cloned from FilterPopup.jsx. */

const FLYER_LOADING_MIN_MS = 2700;
const FLYER_CATEGORIES = ['Music', 'Cards', 'Art', 'Food', 'Nightlife', 'Community'];
const FLYER_CAT_COLORS = {
  'Music': '#D57800',
  'Cards': '#7A99AC',
  'Art': '#FF4F00',
  'Food': '#395542',
  'Nightlife': '#5B7A8D',
  'Community': '#5A7A68',
};
const FLYER_ACCEPTED_TYPES = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp', 'image/heic', 'image/heif'];

function prefersReducedMotion() {
  try { return window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; }
  catch { return false; }
}

/* ── Custom SVG icons ───────────────────────────────────────── */
function FlyerIcon({ size = 56, color = '#D57800', folded = true }) {
  // A rectangle with a folded top-right corner. Toggles to "unfolded" on dragover.
  return (
    <svg width={size} height={size} viewBox="0 0 56 56" fill="none"
      style={{ transition: 'transform 240ms cubic-bezier(0.34, 1.56, 0.64, 1)' }}>
      {/* Main page */}
      <path
        d={folded
          ? "M10 8 L40 8 L48 16 L48 48 L10 48 Z"
          : "M10 8 L48 8 L48 48 L10 48 Z"}
        stroke={color} strokeWidth="2" strokeLinejoin="round" fill="none"
        style={{ transition: 'd 240ms cubic-bezier(0.34, 1.56, 0.64, 1)' }}
      />
      {/* Fold triangle */}
      <path
        d={folded ? "M40 8 L40 16 L48 16" : "M48 8 L48 8 L48 8"}
        stroke={color} strokeWidth="2" strokeLinejoin="round" fill="none"
        opacity={folded ? 1 : 0}
        style={{ transition: 'opacity 240ms ease' }}
      />
      {/* Lines mimicking flyer text */}
      <line x1="16" y1="22" x2="36" y2="22" stroke={color} strokeWidth="1.5" strokeLinecap="round" opacity="0.55" />
      <line x1="16" y1="29" x2="42" y2="29" stroke={color} strokeWidth="1.5" strokeLinecap="round" opacity="0.55" />
      <line x1="16" y1="36" x2="32" y2="36" stroke={color} strokeWidth="1.5" strokeLinecap="round" opacity="0.55" />
    </svg>
  );
}

function CloseIcon({ size = 14, color }) {
  return (
    <svg width={size} height={size} viewBox="0 0 14 14" fill="none">
      <path d="M3 3 L11 11 M11 3 L3 11" stroke={color} strokeWidth="2.2" strokeLinecap="round" />
    </svg>
  );
}

function SparkleIcon({ size = 48, color = '#D57800', glow = true, dark = true }) {
  return (
    <svg width={size} height={size} viewBox="0 0 48 48" fill="none"
      style={{
        filter: glow
          ? (dark ? 'drop-shadow(0 0 24px rgba(213,120,0,0.45))' : 'drop-shadow(0 0 18px rgba(213,120,0,0.30))')
          : 'none',
      }}>
      <path
        d="M24 4 L26 22 L44 24 L26 26 L24 44 L22 26 L4 24 L22 22 Z"
        fill={color}
      />
    </svg>
  );
}

function CopyIcon({ size = 18, color, copied }) {
  if (copied) {
    return (
      <svg width={size} height={size} viewBox="0 0 18 18" fill="none">
        <path d="M3 9 L7 13 L15 5" stroke={color} strokeWidth="2.2"
          strokeLinecap="round" strokeLinejoin="round" />
      </svg>
    );
  }
  return (
    <svg width={size} height={size} viewBox="0 0 18 18" fill="none">
      <rect x="5" y="5" width="9" height="11" rx="1.5" stroke={color} strokeWidth="1.8" />
      <path d="M3 12 L3 3 L11 3" stroke={color} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

/* ── Scan line animation keyframes (injected once) ──────────── */
function ensureFlyerStyles() {
  if (document.getElementById('flyer-upload-styles')) return;
  const style = document.createElement('style');
  style.id = 'flyer-upload-styles';
  style.textContent = `
    @keyframes flyerScanSweep {
      0%   { top: 0%; }
      100% { top: 100%; }
    }
    @keyframes flyerDotPulse {
      0%, 100% { transform: scale(1); }
      50%      { transform: scale(1.25); }
    }
    @keyframes flyerCharFade {
      from { opacity: 0; transform: translateY(2px); }
      to   { opacity: 1; transform: translateY(0); }
    }
    @keyframes flyerSparkleEntry {
      0%   { transform: scale(0.6); }
      60%  { transform: scale(1.1); }
      100% { transform: scale(1.0); }
    }
    @keyframes flyerSparkleSpin {
      from { transform: rotate(0deg); }
      to   { transform: rotate(360deg); }
    }
    @keyframes flyerRipple {
      0%   { box-shadow: 0 0 0 3px var(--international-orange), 0 0 40px rgba(255,79,0,0.45); }
      50%  { box-shadow: 0 0 0 3px rgba(255,79,0,0.25), 0 0 30px rgba(255,79,0,0.15); }
      100% { box-shadow: 0 0 0 3px transparent, 0 0 0 transparent; }
    }
    .flyer-card-pulse {
      animation: flyerRipple 1600ms ease-out 2;
    }
  `;
  document.head.appendChild(style);
}

/* ── Drop zone component ────────────────────────────────────── */
function DropZone({ darkMode, onFile, error, dragOver, setDragOver }) {
  const inputRef = React.useRef(null);
  const reducedMotion = prefersReducedMotion();

  const handleClick = () => inputRef.current?.click();
  const handleFileChange = (e) => {
    const f = e.target.files?.[0];
    if (f) onFile(f);
    e.target.value = '';
  };

  return (
    <div>
      <div
        role="button"
        tabIndex={0}
        onClick={handleClick}
        onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleClick(); } }}
        onDragEnter={(e) => { e.preventDefault(); setDragOver(true); }}
        onDragOver={(e) => { e.preventDefault(); setDragOver(true); }}
        onDragLeave={(e) => { e.preventDefault(); setDragOver(false); }}
        onDrop={(e) => {
          e.preventDefault();
          setDragOver(false);
          const f = e.dataTransfer.files?.[0];
          if (f) onFile(f);
        }}
        aria-label="Drop a flyer image here, or click to pick a file"
        style={{
          width: '100%',
          minHeight: 168,
          border: `1.5px dashed ${
            dragOver
              ? (darkMode ? 'rgba(255,79,0,0.7)' : 'rgba(255,79,0,0.85)')
              : (darkMode ? 'rgba(255,183,120,0.25)' : 'rgba(213,120,0,0.35)')
          }`,
          background: dragOver
            ? (darkMode ? 'rgba(255,79,0,0.08)' : 'rgba(255,79,0,0.06)')
            : (darkMode ? 'rgba(255,255,255,0.02)' : 'rgba(0,0,0,0.015)'),
          borderRadius: 12,
          display: 'flex', flexDirection: 'column',
          alignItems: 'center', justifyContent: 'center',
          padding: '20px',
          cursor: 'pointer',
          transition: reducedMotion ? 'none' : 'border-color 180ms ease, background 180ms ease',
          outline: 'none',
        }}
      >
        <FlyerIcon size={56} color={darkMode ? '#D57800' : '#D57800'} folded={!dragOver || reducedMotion} />
        <div style={{
          marginTop: 14,
          fontFamily: 'var(--font-body)', fontSize: 13, fontWeight: 700,
          letterSpacing: '0.05em', textTransform: 'uppercase',
          color: darkMode ? '#DBC2AF' : '#4A3A2D',
          textAlign: 'center',
        }}>
          {dragOver ? 'let it go' : 'drop a flyer here'}
        </div>
        {!dragOver && (
          <div style={{
            marginTop: 4,
            fontFamily: 'var(--font-body)', fontSize: 11, fontWeight: 500,
            color: darkMode ? 'rgba(255,255,255,0.4)' : 'rgba(0,0,0,0.4)',
            textAlign: 'center',
          }}>
            or paste · or pick a file
          </div>
        )}
      </div>
      <input
        ref={inputRef}
        type="file"
        accept="image/png,image/jpeg,image/webp,image/heic,image/heif"
        onChange={handleFileChange}
        style={{ display: 'none' }}
        aria-hidden="true"
        tabIndex={-1}
      />
      <div style={{
        marginTop: 12,
        fontFamily: 'var(--font-body)', fontSize: 11, fontWeight: 600,
        letterSpacing: '0.05em', textTransform: 'uppercase',
        textAlign: 'center',
        color: error
          ? 'var(--international-orange)'
          : (darkMode ? 'rgba(255,255,255,0.4)' : 'rgba(0,0,0,0.4)'),
      }}>
        {error
          ? error
          : 'jpg, png, heic · 5MB max'}
      </div>
    </div>
  );
}

/* ── Loading state ──────────────────────────────────────────── */
const LOADING_ROWS = [
  { label: 'Finding the title', resolveAt: 900 },
  { label: 'Locking the date', resolveAt: 1500 },
  { label: 'Reading the venue', resolveAt: 2100 },
  { label: 'Sniffing for vibes', resolveAt: 2700 },
];

function LoadingState({ darkMode, previewUrl, startedAt }) {
  const reducedMotion = prefersReducedMotion();
  const [tick, setTick] = React.useState(0);

  React.useEffect(() => {
    if (reducedMotion) return;
    const interval = setInterval(() => setTick(Date.now() - startedAt), 100);
    return () => clearInterval(interval);
  }, [startedAt, reducedMotion]);

  const elapsed = reducedMotion ? FLYER_LOADING_MIN_MS : tick;

  const heading = 'reading the flyer';
  const headingChars = heading.split('');

  return (
    <div style={{ padding: '36px 24px 28px', textAlign: 'center' }}>
      <h2 id="flyer-heading"
        aria-live="polite"
        style={{
          fontFamily: 'var(--font-display)', fontSize: 24, fontWeight: 800,
          letterSpacing: '-0.02em', textTransform: 'lowercase',
          color: darkMode ? '#fff' : '#0A0A0A',
          marginBottom: 24,
        }}>
        {reducedMotion ? heading : headingChars.map((c, i) => (
          <span key={i} style={{
            display: 'inline-block',
            opacity: 0,
            animation: `flyerCharFade 300ms ease forwards`,
            animationDelay: `${i * 30}ms`,
            whiteSpace: 'pre',
          }}>{c}</span>
        ))}
      </h2>

      {/* Flyer thumbnail with scan line */}
      <div style={{
        position: 'relative',
        width: 200, height: 260,
        maxWidth: '60vw',
        margin: '0 auto',
        borderRadius: 8,
        overflow: 'hidden',
        boxShadow: darkMode
          ? 'inset 0 0 0 1px rgba(255,255,255,0.08), 0 8px 32px rgba(0,0,0,0.5)'
          : 'inset 0 0 0 1px rgba(0,0,0,0.08), 0 8px 32px rgba(0,0,0,0.18)',
        background: darkMode ? '#0A0A0A' : '#F5F5F5',
      }}>
        {previewUrl && (
          <img src={previewUrl} alt="" style={{
            width: '100%', height: '100%', objectFit: 'cover',
            display: 'block',
          }} />
        )}
        {!reducedMotion && (
          <div style={{
            position: 'absolute', left: 0, right: 0,
            height: 3,
            background: `linear-gradient(90deg,
              transparent 0%,
              rgba(255,183,120,0) 5%,
              rgba(255,183,120,0.8) 30%,
              var(--international-orange) 50%,
              rgba(255,183,120,0.8) 70%,
              rgba(255,183,120,0) 95%,
              transparent 100%)`,
            boxShadow: darkMode
              ? '0 -8px 24px 4px rgba(255,79,0,0.35), 0 -2px 4px rgba(255,183,120,0.5)'
              : '0 -8px 18px 2px rgba(255,79,0,0.18), 0 -2px 4px rgba(255,183,120,0.25)',
            animation: 'flyerScanSweep 2400ms cubic-bezier(0.42, 0, 0.58, 1) infinite',
            top: 0,
          }} />
        )}
      </div>

      {/* Fact-list resolving rows */}
      <div role="status" aria-live="polite" style={{
        marginTop: 24,
        maxWidth: 360,
        margin: '24px auto 0',
        textAlign: 'left',
      }}>
        {LOADING_ROWS.map((row, i) => {
          const prevResolveAt = i === 0 ? 300 : LOADING_ROWS[i - 1].resolveAt;
          let state = 'pending';
          if (elapsed >= row.resolveAt) state = 'resolved';
          else if (elapsed >= prevResolveAt) state = 'resolving';

          const dotColor =
            state === 'resolved' ? 'var(--international-orange)' :
            state === 'resolving' ? 'var(--amber-gold)' :
            'transparent';
          const labelColor =
            state === 'resolved' ? (darkMode ? '#E2E2E2' : '#1A1A1A') :
            state === 'resolving' ? (darkMode ? '#DBC2AF' : '#4A3A2D') :
            (darkMode ? '#A38D7C' : 'rgba(0,0,0,0.4)');

          return (
            <div key={row.label} style={{
              display: 'flex', alignItems: 'center', gap: 12,
              padding: '6px 0',
            }}>
              <div style={{
                width: 10, height: 10, borderRadius: '50%',
                background: dotColor,
                border: state === 'pending' ? '1.5px solid rgba(255,183,120,0.35)' : 'none',
                animation: !reducedMotion && state === 'resolving' ? 'flyerDotPulse 900ms ease-in-out infinite' : 'none',
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                flexShrink: 0,
              }}>
                {state === 'resolved' && (
                  <svg width="6" height="6" viewBox="0 0 6 6">
                    <path d="M1 3 L2.5 4.5 L5 1.5" stroke="#fff" strokeWidth="1.2"
                      strokeLinecap="round" strokeLinejoin="round" fill="none" />
                  </svg>
                )}
              </div>
              <span style={{
                fontFamily: 'var(--font-body)', fontSize: 14, fontWeight: 600,
                color: labelColor,
                transition: 'color 200ms ease',
              }}>
                {row.label}{state === 'resolving' ? '…' : ''}
              </span>
            </div>
          );
        })}
      </div>
    </div>
  );
}

/* ── Review state ───────────────────────────────────────────── */
function FieldMarker({ kind, darkMode }) {
  // kind: 'extracted' | 'edited' | 'optional' | 'missing'
  const labels = {
    extracted: 'EXTRACTED',
    edited: 'YOU EDITED',
    optional: 'OPTIONAL',
    missing: 'MISSING',
  };
  const colors = {
    extracted: 'var(--amber-gold)',
    edited: 'var(--fog-blue)',
    optional: darkMode ? '#A38D7C' : 'rgba(0,0,0,0.4)',
    missing: 'var(--international-orange)',
  };
  const dotColor = colors[kind];
  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 8,
      marginBottom: 6,
    }}>
      <div style={{
        width: 8, height: 8, borderRadius: '50%',
        background: kind === 'optional' ? 'transparent' : dotColor,
        border: kind === 'optional' ? `1.5px solid ${dotColor}` : 'none',
      }} />
      <span style={{
        fontFamily: 'var(--font-body)', fontSize: 10, fontWeight: 700,
        letterSpacing: '0.08em', textTransform: 'uppercase',
        color: dotColor,
        transition: 'color 200ms ease',
      }}>{labels[kind]}</span>
    </div>
  );
}

function ReviewState({ darkMode, radius, extracted, onCancel, onSubmit, submitting }) {
  const [title, setTitle] = React.useState(extracted.title || '');
  const [date, setDate] = React.useState(extracted.date || '');
  const [time, setTime] = React.useState(extracted.time || '');
  const [venue, setVenue] = React.useState(extracted.venue || '');
  const [category, setCategory] = React.useState(() => {
    const c = (extracted.category || 'community').toLowerCase();
    const found = FLYER_CATEGORIES.find(x => x.toLowerCase() === c);
    return found || 'Community';
  });
  const [handle, setHandle] = React.useState('');
  const [edits, setEdits] = React.useState({});

  const markEdit = (field, val, original) => {
    if (val !== (original || '')) {
      setEdits(prev => ({ ...prev, [field]: true }));
    }
  };

  const inputStyle = {
    width: '100%',
    background: darkMode ? 'rgba(255,255,255,0.04)' : 'rgba(0,0,0,0.025)',
    border: darkMode ? '1px solid rgba(255,255,255,0.10)' : '1px solid rgba(0,0,0,0.09)',
    borderRadius: 10,
    padding: '12px 14px',
    minHeight: 44,
    fontFamily: 'var(--font-body)', fontSize: 15, fontWeight: 500,
    color: darkMode ? '#E2E2E2' : '#1A1A1A',
    caretColor: 'var(--international-orange)',
    outline: 'none',
    transition: 'border-color 160ms, background 160ms',
    boxSizing: 'border-box',
  };

  const focusStyle = (e) => {
    e.target.style.borderColor = 'var(--international-orange)';
    e.target.style.background = darkMode ? 'rgba(255,255,255,0.07)' : 'rgba(0,0,0,0.045)';
  };
  const blurStyle = (e) => {
    e.target.style.borderColor = darkMode ? 'rgba(255,255,255,0.10)' : 'rgba(0,0,0,0.09)';
    e.target.style.background = darkMode ? 'rgba(255,255,255,0.04)' : 'rgba(0,0,0,0.025)';
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    if (!title.trim()) return;
    onSubmit({
      title: title.trim(),
      date: date.trim(),
      time: time.trim(),
      venue: venue.trim(),
      category: category.toLowerCase(),
      handle: handle.trim().replace(/^@+/, '').slice(0, 30),
    });
  };

  return (
    <form onSubmit={handleSubmit} style={{ padding: '32px 24px 28px' }}>
      <h2 style={{
        fontFamily: 'var(--font-display)', fontSize: 24, fontWeight: 800,
        letterSpacing: '-0.02em', textTransform: 'lowercase',
        color: darkMode ? '#fff' : '#0A0A0A',
        margin: 0,
      }}>here's what we got</h2>
      <p style={{
        fontFamily: 'var(--font-body)', fontSize: 13, fontWeight: 500,
        color: darkMode ? 'rgba(255,255,255,0.55)' : 'rgba(0,0,0,0.55)',
        margin: '4px 0 22px',
      }}>fix anything we got wrong before it goes live.</p>

      {/* Title */}
      <div style={{ marginBottom: 14 }}>
        <FieldMarker
          kind={edits.title ? 'edited' : (extracted.title ? 'extracted' : 'missing')}
          darkMode={darkMode}
        />
        <label htmlFor="flyer-title" style={{
          position: 'absolute', width: 1, height: 1, overflow: 'hidden', clip: 'rect(0 0 0 0)',
        }}>Event title</label>
        <input
          id="flyer-title"
          type="text"
          value={title}
          autoFocus
          placeholder={extracted.title ? '' : 'what\'s it called'}
          onChange={(e) => { setTitle(e.target.value); markEdit('title', e.target.value, extracted.title); }}
          onFocus={focusStyle} onBlur={blurStyle}
          style={inputStyle}
        />
      </div>

      {/* Date + Time row */}
      <div style={{ display: 'flex', gap: 8, marginBottom: 14 }}>
        <div style={{ flex: 2 }}>
          <FieldMarker kind={edits.date ? 'edited' : (extracted.date ? 'extracted' : 'missing')} darkMode={darkMode} />
          <label htmlFor="flyer-date" style={{ position: 'absolute', width: 1, height: 1, overflow: 'hidden', clip: 'rect(0 0 0 0)' }}>Date</label>
          <input
            id="flyer-date" type="text" value={date}
            placeholder="apr 25"
            onChange={(e) => { setDate(e.target.value); markEdit('date', e.target.value, extracted.date); }}
            onFocus={focusStyle} onBlur={blurStyle}
            style={inputStyle}
          />
        </div>
        <div style={{ flex: 1 }}>
          <FieldMarker kind={edits.time ? 'edited' : (extracted.time ? 'extracted' : 'optional')} darkMode={darkMode} />
          <label htmlFor="flyer-time" style={{ position: 'absolute', width: 1, height: 1, overflow: 'hidden', clip: 'rect(0 0 0 0)' }}>Time</label>
          <input
            id="flyer-time" type="text" value={time}
            placeholder="9pm"
            onChange={(e) => { setTime(e.target.value); markEdit('time', e.target.value, extracted.time); }}
            onFocus={focusStyle} onBlur={blurStyle}
            style={inputStyle}
          />
        </div>
      </div>

      {/* Venue */}
      <div style={{ marginBottom: 14 }}>
        <FieldMarker kind={edits.venue ? 'edited' : (extracted.venue ? 'extracted' : 'missing')} darkMode={darkMode} />
        <label htmlFor="flyer-venue" style={{ position: 'absolute', width: 1, height: 1, overflow: 'hidden', clip: 'rect(0 0 0 0)' }}>Venue</label>
        <input
          id="flyer-venue" type="text" value={venue}
          placeholder="where it's at"
          onChange={(e) => { setVenue(e.target.value); markEdit('venue', e.target.value, extracted.venue); }}
          onFocus={focusStyle} onBlur={blurStyle}
          style={inputStyle}
        />
      </div>

      {/* Category chips */}
      <div style={{ marginBottom: 14 }}>
        <FieldMarker kind={edits.category ? 'edited' : 'extracted'} darkMode={darkMode} />
        <div role="radiogroup" aria-label="Category" style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
          {FLYER_CATEGORIES.map(cat => {
            const sel = cat === category;
            return (
              <button
                key={cat}
                type="button"
                role="radio"
                aria-checked={sel}
                onClick={() => { setCategory(cat); markEdit('category', cat, extracted.category); }}
                style={{
                  padding: '7px 14px',
                  minHeight: 36,
                  borderRadius: 100,
                  fontFamily: 'var(--font-body)', fontSize: 11, fontWeight: 700,
                  letterSpacing: '0.05em', textTransform: 'uppercase',
                  cursor: 'pointer',
                  border: sel
                    ? `1.5px solid ${FLYER_CAT_COLORS[cat]}`
                    : (darkMode ? '1.5px solid rgba(255,255,255,0.12)' : '1.5px solid rgba(0,0,0,0.1)'),
                  background: sel
                    ? FLYER_CAT_COLORS[cat]
                    : (darkMode ? 'rgba(255,255,255,0.04)' : 'rgba(0,0,0,0.03)'),
                  color: sel ? '#fff' : (darkMode ? 'rgba(255,255,255,0.6)' : 'rgba(0,0,0,0.55)'),
                  transition: 'all 0.2s cubic-bezier(0.16, 1, 0.3, 1)',
                }}
              >{cat}</button>
            );
          })}
        </div>
      </div>

      {/* Handle */}
      <div style={{ marginBottom: 22 }}>
        <FieldMarker kind="optional" darkMode={darkMode} />
        <label htmlFor="flyer-handle" style={{ position: 'absolute', width: 1, height: 1, overflow: 'hidden', clip: 'rect(0 0 0 0)' }}>Posted by handle</label>
        <div style={{ position: 'relative' }}>
          <span style={{
            position: 'absolute', left: 14, top: '50%', transform: 'translateY(-50%)',
            fontFamily: 'var(--font-body)', fontSize: 15, fontWeight: 500,
            color: darkMode ? 'rgba(255,255,255,0.5)' : 'rgba(0,0,0,0.4)',
            pointerEvents: 'none',
          }}>@</span>
          <input
            id="flyer-handle" type="text" value={handle}
            maxLength={30}
            placeholder="yourhandle"
            onChange={(e) => setHandle(e.target.value.replace(/^@+/, ''))}
            onFocus={focusStyle} onBlur={blurStyle}
            style={{ ...inputStyle, paddingLeft: 28 }}
          />
        </div>
        <div style={{
          marginTop: 4,
          fontFamily: 'var(--font-body)', fontSize: 11, fontWeight: 500,
          color: darkMode ? 'rgba(255,255,255,0.4)' : 'rgba(0,0,0,0.4)',
        }}>optional — your @ shows on the event if you want credit.</div>
      </div>

      {/* Actions */}
      <div style={{ display: 'flex', gap: 8 }}>
        <button
          type="button"
          onClick={onCancel}
          disabled={submitting}
          style={{
            flex: 1,
            padding: '12px 16px', minHeight: 44,
            borderRadius: Math.min(radius, 12),
            background: 'transparent',
            border: darkMode ? '1px solid rgba(255,255,255,0.08)' : '1px solid rgba(0,0,0,0.07)',
            color: darkMode ? 'rgba(255,255,255,0.45)' : 'rgba(0,0,0,0.4)',
            fontFamily: 'var(--font-body)', fontSize: 12, fontWeight: 700,
            letterSpacing: '0.04em', textTransform: 'uppercase',
            cursor: submitting ? 'not-allowed' : 'pointer',
            opacity: submitting ? 0.5 : 1,
            transition: 'all 0.2s',
          }}
        >Cancel</button>
        <button
          type="submit"
          disabled={submitting || !title.trim()}
          style={{
            flex: 2,
            padding: '12px 16px', minHeight: 44,
            borderRadius: Math.min(radius, 12),
            background: 'var(--international-orange)',
            border: 'none',
            color: '#fff',
            fontFamily: 'var(--font-body)', fontSize: 12, fontWeight: 700,
            letterSpacing: '0.04em', textTransform: 'uppercase',
            cursor: submitting || !title.trim() ? 'not-allowed' : 'pointer',
            opacity: submitting || !title.trim() ? 0.5 : 1,
            transition: 'all 0.2s',
            boxShadow: '0 4px 16px rgba(255,79,0,0.25)',
          }}
        >{submitting ? 'putting it on…' : 'PUT IT ON ›'}</button>
      </div>
    </form>
  );
}

/* ── Success state ──────────────────────────────────────────── */
function SuccessState({ darkMode, radius, event, onSeeInGrid, onAnother, onClose }) {
  const [copied, setCopied] = React.useState(false);
  const reducedMotion = prefersReducedMotion();

  const slug = event.slug || event.id;
  const fullUrl = `onsumn.com/e/${slug}`;
  const shareUrl = `https://onsumn.com/e/${slug}`;

  const copy = async () => {
    try {
      await navigator.clipboard.writeText(shareUrl);
      setCopied(true);
      setTimeout(() => setCopied(false), 2500);
    } catch {
      // Fallback: select the text
      try {
        const sel = window.getSelection();
        const range = document.createRange();
        range.selectNodeContents(document.getElementById('flyer-share-link'));
        sel?.removeAllRanges(); sel?.addRange(range);
      } catch { /* ignore */ }
    }
  };

  return (
    <div style={{ padding: '36px 24px 28px', textAlign: 'center' }}>
      <div style={{
        display: 'inline-block',
        animation: reducedMotion ? 'none' : 'flyerSparkleEntry 480ms cubic-bezier(0.34, 1.56, 0.64, 1)',
      }}>
        <div style={{
          animation: reducedMotion ? 'none' : 'flyerSparkleSpin 8s linear infinite',
          display: 'inline-block',
        }}>
          <SparkleIcon size={48} color="#D57800" dark={darkMode} />
        </div>
      </div>
      <h2 style={{
        fontFamily: 'var(--font-display)', fontSize: 32, fontWeight: 800,
        letterSpacing: '-0.02em', textTransform: 'lowercase',
        color: darkMode ? '#fff' : '#0A0A0A',
        margin: '14px 0 4px',
      }}>it's on the map.</h2>
      <div style={{
        fontFamily: 'var(--font-body)', fontSize: 16, fontWeight: 600,
        color: darkMode ? 'rgba(255,255,255,0.6)' : 'rgba(0,0,0,0.55)',
        whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
        marginBottom: 6,
      }}>{event.title}</div>
      <div style={{
        fontFamily: 'var(--font-body)', fontSize: 12, fontWeight: 500,
        color: darkMode ? 'rgba(255,255,255,0.4)' : 'rgba(0,0,0,0.4)',
        marginBottom: 24,
      }}>live now. share the link with the crew.</div>

      {/* Share link block */}
      <div style={{
        width: '100%',
        height: 52,
        borderRadius: 14,
        background: darkMode ? 'rgba(255,183,120,0.06)' : 'rgba(213,120,0,0.08)',
        border: darkMode ? '1px solid rgba(255,183,120,0.18)' : '1px solid rgba(213,120,0,0.22)',
        display: 'flex', alignItems: 'center',
        padding: '0 4px 0 0',
        boxSizing: 'border-box',
      }}>
        <span id="flyer-share-link" style={{
          flex: 1,
          paddingLeft: 16,
          fontFamily: 'var(--font-body)', fontSize: 14, fontWeight: 600,
          color: darkMode ? '#E2E2E2' : '#1A1A1A',
          whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
          textAlign: 'left',
          userSelect: 'all',
        }}>{fullUrl}</span>
        <button
          type="button"
          onClick={copy}
          aria-label={copied ? 'Copied' : 'Copy share link'}
          style={{
            width: 44, height: 44,
            border: 'none',
            borderRadius: 10,
            background: 'transparent',
            cursor: 'pointer',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            color: 'var(--amber-gold)',
            fontFamily: 'var(--font-body)', fontSize: 11, fontWeight: 700,
            letterSpacing: '0.05em',
            transition: 'background 160ms ease',
          }}
          onMouseEnter={e => { e.currentTarget.style.background = 'rgba(213,120,0,0.10)'; }}
          onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; }}
        >
          <CopyIcon size={18} color="currentColor" copied={copied} />
        </button>
      </div>
      <div style={{
        height: 18, marginTop: 6,
        fontFamily: 'var(--font-body)', fontSize: 12, fontWeight: 600,
        color: 'var(--eucalyptus-green-light)',
        opacity: copied ? 1 : 0,
        transition: 'opacity 200ms ease',
      }}>{copied ? 'COPIED — paste it in the group chat' : ''}</div>

      {/* Actions */}
      <div style={{ marginTop: 20 }}>
        <button
          type="button"
          onClick={onSeeInGrid}
          style={{
            padding: '14px 24px',
            minHeight: 48,
            borderRadius: Math.min(radius, 12),
            background: 'var(--international-orange)',
            border: 'none',
            color: '#fff',
            fontFamily: 'var(--font-body)', fontSize: 12, fontWeight: 700,
            letterSpacing: '0.04em', textTransform: 'uppercase',
            cursor: 'pointer',
            transition: 'all 0.2s',
            boxShadow: '0 4px 16px rgba(255,79,0,0.25)',
          }}
        >SEE IT</button>
      </div>
      <div style={{ marginTop: 16 }}>
        <button
          type="button"
          onClick={onAnother}
          style={{
            background: 'none', border: 'none',
            fontFamily: 'var(--font-body)', fontSize: 13, fontWeight: 600,
            color: 'var(--amber-gold)',
            cursor: 'pointer',
            padding: '8px 12px',
            textDecoration: 'underline',
            textUnderlineOffset: 3,
          }}
        >put on another</button>
      </div>
    </div>
  );
}

/* ── Error state (replaces drop zone or review) ─────────────── */
function ErrorState({ darkMode, radius, kind, message, onRetry, onCancel }) {
  // kind: 'rate' | 'global' | 'rejected' | 'network' | 'unknown'
  const titles = {
    rate: "you've put on 5 today.",
    global: "the day's full.",
    rejected: "couldn't read this as an event flyer.",
    network: "connection dropped.",
    unknown: "didn't go through.",
  };
  const bodies = {
    rate: 'come back tomorrow with more.',
    global: '200 flyers got dropped today. try tomorrow.',
    rejected: 'try a clearer pic, or one with the date and venue visible.',
    network: 'try again in a sec.',
    unknown: message || 'try again, or hit us up if it keeps failing.',
  };

  return (
    <div style={{ padding: '40px 24px 28px', textAlign: 'center' }}>
      <h2 style={{
        fontFamily: 'var(--font-display)', fontSize: 24, fontWeight: 800,
        letterSpacing: '-0.02em', textTransform: 'lowercase',
        color: darkMode ? '#fff' : '#0A0A0A',
        margin: '0 0 8px',
      }}>{titles[kind] || titles.unknown}</h2>
      <p style={{
        fontFamily: 'var(--font-body)', fontSize: 14, fontWeight: 500,
        color: darkMode ? 'rgba(255,255,255,0.55)' : 'rgba(0,0,0,0.55)',
        margin: '0 0 28px',
      }}>{bodies[kind] || bodies.unknown}</p>
      <div style={{ display: 'flex', gap: 8, justifyContent: 'center' }}>
        <button
          type="button"
          onClick={onCancel}
          style={{
            padding: '12px 22px', minHeight: 44,
            borderRadius: Math.min(radius, 12),
            background: 'transparent',
            border: darkMode ? '1px solid rgba(255,255,255,0.08)' : '1px solid rgba(0,0,0,0.07)',
            color: darkMode ? 'rgba(255,255,255,0.55)' : 'rgba(0,0,0,0.5)',
            fontFamily: 'var(--font-body)', fontSize: 12, fontWeight: 700,
            letterSpacing: '0.04em', textTransform: 'uppercase',
            cursor: 'pointer',
          }}
        >close</button>
        {(kind === 'network' || kind === 'rejected') && onRetry && (
          <button
            type="button"
            onClick={onRetry}
            style={{
              padding: '12px 22px', minHeight: 44,
              borderRadius: Math.min(radius, 12),
              background: 'var(--international-orange)',
              border: 'none',
              color: '#fff',
              fontFamily: 'var(--font-body)', fontSize: 12, fontWeight: 700,
              letterSpacing: '0.04em', textTransform: 'uppercase',
              cursor: 'pointer',
              boxShadow: '0 4px 16px rgba(255,79,0,0.25)',
            }}
          >{kind === 'rejected' ? 'try another flyer ›' : 'retry ›'}</button>
        )}
      </div>
    </div>
  );
}

/* ── Main FlyerUpload component ─────────────────────────────── */
function FlyerUpload({ open, onClose, darkMode, radius = 16 }) {
  const [state, setState] = React.useState('empty'); // empty | loading | review | success | error
  const [errorKind, setErrorKind] = React.useState('unknown');
  const [errorMsg, setErrorMsg] = React.useState('');
  const [inlineError, setInlineError] = React.useState(''); // file-size etc.
  const [imageFile, setImageFile] = React.useState(null);
  const [previewUrl, setPreviewUrl] = React.useState(null);
  const [extracted, setExtracted] = React.useState(null);
  const [createdEvent, setCreatedEvent] = React.useState(null);
  const [submitting, setSubmitting] = React.useState(false);
  const [dragOver, setDragOver] = React.useState(false);
  const [visible, setVisible] = React.useState(false);
  const [loadingStartedAt, setLoadingStartedAt] = React.useState(0);
  const [turnstileToken, setTurnstileToken] = React.useState(null);
  const turnstileRef = React.useRef(null);
  const turnstileWidgetIdRef = React.useRef(null);

  const isMobile = typeof window !== 'undefined' && window.matchMedia
    ? window.matchMedia('(max-width: 640px)').matches
    : false;

  React.useEffect(() => { ensureFlyerStyles(); }, []);

  React.useEffect(() => {
    if (open) {
      requestAnimationFrame(() => setVisible(true));
    } else {
      setVisible(false);
      // Reset internal state when closed.
      setTimeout(() => {
        setState('empty');
        setErrorKind('unknown');
        setErrorMsg('');
        setInlineError('');
        setImageFile(null);
        if (previewUrl) URL.revokeObjectURL(previewUrl);
        setPreviewUrl(null);
        setExtracted(null);
        setCreatedEvent(null);
        setSubmitting(false);
        setDragOver(false);
      }, 220);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [open]);

  // Render the Cloudflare Turnstile widget when the Empty state is open.
  // The widget captures a human-verification token that we send with the upload.
  React.useEffect(() => {
    if (!open || state !== 'empty') return;
    if (!window.TURNSTILE_SITE_KEY || !turnstileRef.current) return;

    let cancelled = false;
    setTurnstileToken(null);

    const renderWhenReady = () => {
      if (cancelled) return;
      if (!window.turnstile || !turnstileRef.current) {
        setTimeout(renderWhenReady, 200);
        return;
      }
      try {
        turnstileWidgetIdRef.current = window.turnstile.render(turnstileRef.current, {
          sitekey: window.TURNSTILE_SITE_KEY,
          callback: (token) => { if (!cancelled) setTurnstileToken(token); },
          'error-callback': () => { if (!cancelled) setTurnstileToken(null); },
          'expired-callback': () => { if (!cancelled) setTurnstileToken(null); },
          theme: darkMode ? 'dark' : 'light',
          size: 'flexible',
          appearance: 'interaction-only',
        });
      } catch (e) {
        // ignore — widget may already be rendered on remount
      }
    };

    renderWhenReady();

    return () => {
      cancelled = true;
      if (turnstileWidgetIdRef.current && window.turnstile) {
        try { window.turnstile.remove(turnstileWidgetIdRef.current); } catch (e) {}
        turnstileWidgetIdRef.current = null;
      }
    };
  }, [open, state, darkMode]);

  // ESC + paste handlers
  React.useEffect(() => {
    if (!open) return;
    const onKey = (e) => {
      if (e.key === 'Escape' && state !== 'loading') onClose();
    };
    const onPaste = (e) => {
      const items = e.clipboardData?.items || [];
      for (const it of items) {
        if (it.type && it.type.startsWith('image/')) {
          const f = it.getAsFile();
          if (f) {
            e.preventDefault();
            handleFile(f);
            break;
          }
        }
      }
    };
    window.addEventListener('keydown', onKey);
    window.addEventListener('paste', onPaste);
    return () => {
      window.removeEventListener('keydown', onKey);
      window.removeEventListener('paste', onPaste);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [open, state]);

  const handleFile = (file) => {
    setInlineError('');
    if (!file) return;
    if (file.size > 5 * 1024 * 1024) {
      setInlineError('that flyer\'s too big. max 5MB.');
      return;
    }
    if (!file.type.startsWith('image/') && !FLYER_ACCEPTED_TYPES.includes(file.type)) {
      setInlineError('image only — jpg, png, heic.');
      return;
    }
    setImageFile(file);
    if (previewUrl) URL.revokeObjectURL(previewUrl);
    setPreviewUrl(URL.createObjectURL(file));
    runExtraction(file);
  };

  const runExtraction = async (file) => {
    const startedAt = Date.now();
    setLoadingStartedAt(startedAt);
    setState('loading');

    const reducedMotion = prefersReducedMotion();
    const minDelay = reducedMotion ? 0 : FLYER_LOADING_MIN_MS;

    const fd = new FormData();
    fd.append('image', file);
    fd.append('turnstileToken', turnstileToken || '');

    let apiResult = null;
    let apiError = null;

    try {
      const apiPromise = fetch('/api/flyer', { method: 'POST', body: fd })
        .then(async (res) => ({ status: res.status, body: await res.json().catch(() => ({})) }));
      const minPromise = new Promise(resolve => setTimeout(resolve, minDelay));
      const [res] = await Promise.all([apiPromise, minPromise]);
      apiResult = res;
    } catch (e) {
      apiError = e;
      // Still hold the minimum animation
      const remaining = Math.max(0, minDelay - (Date.now() - startedAt));
      await new Promise(resolve => setTimeout(resolve, remaining));
    }

    if (apiError || !apiResult) {
      setState('error');
      setErrorKind('network');
      return;
    }

    const { status, body } = apiResult;
    if (status === 200 && body.event) {
      // Show review form
      setExtracted({
        title: body.event.title || '',
        date: body.event.date || '',
        time: body.event.time || '',
        venue: body.event.venue || '',
        category: body.event.category || 'community',
        slug: body.event.slug,
        _serverEvent: body.event,
      });
      setState('review');
      return;
    }

    if (status === 429) {
      setState('error');
      setErrorKind(body.error === 'global_rate_limited' ? 'global' : 'rate');
      return;
    }
    if (status === 422 || status === 502) {
      setState('error');
      setErrorKind('rejected');
      return;
    }
    if (status === 413) {
      setState('empty');
      setInlineError('that flyer\'s too big. max 5MB.');
      return;
    }
    if (status === 400) {
      setState('empty');
      setInlineError(body.message || 'something\'s off with that file.');
      return;
    }
    setState('error');
    setErrorKind('unknown');
    setErrorMsg(body.message || '');
  };

  const handleReviewSubmit = (edits) => {
    setSubmitting(true);
    // The server already wrote the event in /api/flyer; we treat that response
    // as the canonical record but reflect the user's edits in the local copy
    // that goes into the grid via addEvent().
    const server = extracted._serverEvent || {};
    const eventForGrid = {
      ...server,
      title: edits.title,
      date: edits.date || server.date,
      time: edits.time || server.time,
      venue: edits.venue || server.venue,
      category: (edits.category || server.category || 'community').toUpperCase(),
      handle: edits.handle || server.handle || null,
      // Derive city tag from venue (best-effort) for the carousel rows.
      tag: server.tag || (edits.venue || server.venue || '').toUpperCase().includes('OAKLAND')
        ? 'THE TOWN'
        : (edits.venue || server.venue || '').toUpperCase().split(',').pop().trim() || 'BAY AREA',
      city: server.city || guessCity(edits.venue || server.venue || ''),
      desc: server.desc || '',
      id: server.id || server.slug,
      provenance: 'user',
      source: server.source || 'user-ocr',
      // Pin near Bay Area center if we have no coords (so it doesn't crash the map filter).
      lat: server.lat || 37.7749,
      lng: server.lng || -122.4194,
    };
    // Inject into the live grid
    try { window.addEvent && window.addEvent(eventForGrid); } catch (e) { console.error(e); }
    setCreatedEvent(eventForGrid);
    setSubmitting(false);
    setState('success');
  };

  const handleSeeInGrid = () => {
    const slug = createdEvent?.slug || createdEvent?.id;
    onClose();
    setTimeout(() => {
      if (slug && typeof window.scrollToEvent === 'function') {
        window.scrollToEvent(slug);
      }
    }, 260);
  };

  const handleAnother = () => {
    if (previewUrl) URL.revokeObjectURL(previewUrl);
    setPreviewUrl(null);
    setImageFile(null);
    setExtracted(null);
    setCreatedEvent(null);
    setInlineError('');
    setState('empty');
  };

  if (!open) return null;

  const reducedMotion = prefersReducedMotion();
  const headingId = 'flyer-modal-heading';

  // Panel transform
  const panelTransform = isMobile
    ? (visible ? 'translateY(0)' : 'translateY(20px)')
    : (visible ? 'translate(-50%, -50%) scale(1)' : 'translate(-50%, -48%) scale(0.97)');

  return (
    <React.Fragment>
      {/* Backdrop */}
      <div
        onClick={() => { if (state !== 'loading') onClose(); }}
        aria-label="Close flyer upload"
        style={{
          position: 'fixed', inset: 0, zIndex: 9990,
          background: darkMode ? 'rgba(0,0,0,0.55)' : 'rgba(20,20,20,0.40)',
          backdropFilter: 'blur(14px)', WebkitBackdropFilter: 'blur(14px)',
          opacity: visible ? 1 : 0,
          transition: reducedMotion ? 'opacity 150ms ease' : 'opacity 200ms ease',
          pointerEvents: visible ? 'auto' : 'none',
        }}
      />

      {/* Panel */}
      <div
        role="dialog"
        aria-modal="true"
        aria-labelledby={headingId}
        style={{
          position: 'fixed',
          ...(isMobile
            ? { left: 0, right: 0, bottom: 0, top: 'auto' }
            : { top: '50%', left: '50%' }),
          transform: panelTransform,
          zIndex: 9991,
          width: isMobile ? '100vw' : 'min(560px, 92vw)',
          maxHeight: isMobile ? '100dvh' : '86vh',
          height: isMobile ? '100dvh' : 'auto',
          minHeight: 420,
          overflowY: 'auto',
          opacity: visible ? 1 : 0,
          pointerEvents: visible ? 'auto' : 'none',
          transition: reducedMotion
            ? 'opacity 150ms ease'
            : `opacity 280ms ease, transform ${isMobile ? 320 : 280}ms cubic-bezier(0.16, 1, 0.3, 1)`,
          ...glassStyle(darkMode, 'panel'),
          background: darkMode
            ? 'linear-gradient(145deg, rgba(18,20,20,0.97), rgba(10,10,10,0.98))'
            : 'linear-gradient(145deg, rgba(255,255,255,0.97), rgba(248,248,248,0.98))',
          border: darkMode ? '1px solid rgba(255,255,255,0.08)' : '1px solid rgba(0,0,0,0.06)',
          borderRadius: isMobile ? 0 : Math.min(radius, 20),
          padding: 0,
          paddingBottom: isMobile ? 'env(safe-area-inset-bottom)' : 0,
        }}
      >
        {/* Close button */}
        <button
          type="button"
          onClick={onClose}
          disabled={state === 'loading'}
          aria-label="Close"
          style={{
            position: 'absolute', top: 14, right: 14,
            width: 36, height: 36, borderRadius: '50%',
            background: darkMode ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.05)',
            border: 'none',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            cursor: state === 'loading' ? 'wait' : 'pointer',
            opacity: state === 'loading' ? 0.3 : 0.7,
            transition: 'opacity 150ms ease, background 150ms ease',
            zIndex: 10,
          }}
          onMouseEnter={e => { if (state !== 'loading') e.currentTarget.style.background = darkMode ? 'rgba(255,255,255,0.14)' : 'rgba(0,0,0,0.09)'; }}
          onMouseLeave={e => { e.currentTarget.style.background = darkMode ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.05)'; }}
        >
          <CloseIcon size={14} color={darkMode ? '#fff' : '#0A0A0A'} />
        </button>

        {/* State content */}
        {state === 'empty' && (
          <div style={{ padding: '40px 24px 28px' }}>
            <h2 id={headingId} style={{
              fontFamily: 'var(--font-display)', fontSize: isMobile ? 22 : 28, fontWeight: 800,
              letterSpacing: '-0.02em',
              color: darkMode ? '#fff' : '#0A0A0A',
              textAlign: 'center',
              margin: '0 0 6px',
            }}>Drop the flyer.</h2>
            <p style={{
              fontFamily: 'var(--font-body)', fontSize: isMobile ? 13 : 14, fontWeight: 500,
              color: darkMode ? 'rgba(255,255,255,0.55)' : 'rgba(0,0,0,0.5)',
              textAlign: 'center',
              margin: '0 0 24px',
            }}>we'll read it and put it on the map.</p>
            <DropZone
              darkMode={darkMode}
              onFile={handleFile}
              error={inlineError}
              dragOver={dragOver}
              setDragOver={setDragOver}
            />
            <div
              ref={turnstileRef}
              className="cf-turnstile-host"
              aria-hidden="true"
              style={{ marginTop: 16, display: 'flex', justifyContent: 'center', minHeight: 0 }}
            />
          </div>
        )}

        {state === 'loading' && (
          <LoadingState
            darkMode={darkMode}
            previewUrl={previewUrl}
            startedAt={loadingStartedAt}
          />
        )}

        {state === 'review' && extracted && (
          <ReviewState
            darkMode={darkMode}
            radius={radius}
            extracted={extracted}
            submitting={submitting}
            onCancel={onClose}
            onSubmit={handleReviewSubmit}
          />
        )}

        {state === 'success' && createdEvent && (
          <SuccessState
            darkMode={darkMode}
            radius={radius}
            event={createdEvent}
            onSeeInGrid={handleSeeInGrid}
            onAnother={handleAnother}
            onClose={onClose}
          />
        )}

        {state === 'error' && (
          <ErrorState
            darkMode={darkMode}
            radius={radius}
            kind={errorKind}
            message={errorMsg}
            onRetry={errorKind === 'network' && imageFile
              ? () => runExtraction(imageFile)
              : (errorKind === 'rejected' ? handleAnother : null)}
            onCancel={onClose}
          />
        )}
      </div>
    </React.Fragment>
  );
}

/* Best-effort city tagging — falls through to 'SF' for Bay Area. */
function guessCity(venue) {
  const v = (venue || '').toUpperCase();
  if (v.includes('OAKLAND')) return 'OAKLAND';
  if (v.includes('SAN JOSE')) return 'SAN JOSE';
  if (v.includes('VALLEJO')) return 'VALLEJO';
  if (v.includes('RICHMOND')) return 'RICHMOND';
  if (v.includes('DALY CITY')) return 'DALY CITY';
  if (v.includes('BERKELEY')) return 'BERKELEY';
  return 'SF';
}

FlyerUpload.propTypes = {
  open: PropTypes.bool.isRequired,
  onClose: PropTypes.func.isRequired,
  darkMode: PropTypes.bool.isRequired,
  radius: PropTypes.number,
};

window.FlyerUpload = FlyerUpload;
