// screens.jsx — the four screens

const { useState: useState_S, useEffect: useEffect_S, useRef: useRef_S, useMemo: useMemo_S } = React;

// ============================================================
// LANDING
// ============================================================
function LandingScreen({ onStart, onDemo, onResumeDraft }) {
  const [drafts, setDrafts] = useState_S(() => window.loadDrafts());

  const handleDeleteDraft = (e, id) => {
    e.stopPropagation();
    window.deleteDraft(id);
    setDrafts(window.loadDrafts());
  };

  return (
    <div className="landing" data-screen-label="01 Landing">
      <div className="page">
        <div className="hero-grid">
          {/* LEFT: title + cta */}
          <div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 24 }}>
              <span className="mono">{window.RWL_VERSION} · still chaotic</span>
              <span className="sticker pink" style={{ transform: 'rotate(-3deg)' }}>now in beta ✦</span>
            </div>

            <h1 className="hero-title">
              <span className="stretchy">r we</span><br/>
              <span className="lost">lost<span className="qm">?</span></span>
            </h1>
            <Squiggle color="var(--accent)" />

            <p className="hero-sub">
              the trip planner for people who saved <b>47 cafés</b> in lisbon and <b>9 tattoo studios</b> in mexico city and a <b>maybe-haunted</b> piano bar in lyon.
            </p>
            <p className="hero-sub" style={{ marginTop: 12, fontSize: 17 }}>
              paste your bookmarks. tell us when. we'll route the chaos.
            </p>

            <div className="hero-cta-row">
              <button className="btn primary" onClick={onStart}>
                start planning →
              </button>
              <button className="btn ghost" onClick={onDemo} style={{ whiteSpace: 'nowrap' }}>
                try lisbon demo →
              </button>
              <span className="mono" style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
                <span style={{ display: 'inline-block', width: 8, height: 8, background: 'var(--teal)', borderRadius: '50%', border: '1.5px solid var(--ink-line)' }}></span>
                free · no signup · no email guilt-trip
              </span>
            </div>

            {drafts.length > 0 && (
              <div style={{ marginTop: 28, paddingTop: 20, borderTop: '1.5px solid var(--ink-line)' }}>
                <p className="mono" style={{ fontSize: 11, color: 'var(--ink-soft)', letterSpacing: '0.08em', marginBottom: 10 }}>CONTINUE PLANNING</p>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                  {drafts.map(d => (
                    <div
                      key={d.id}
                      onClick={() => onResumeDraft(d)}
                      style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '10px 14px', border: '1.5px solid var(--ink-line)', borderRadius: 12, cursor: 'pointer', background: 'var(--bg)', transition: 'border-color 0.15s' }}
                      onMouseEnter={e => e.currentTarget.style.borderColor = 'var(--accent)'}
                      onMouseLeave={e => e.currentTarget.style.borderColor = 'var(--ink-line)'}
                    >
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 15, letterSpacing: '-0.01em', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{d.label}</div>
                        <div className="mono" style={{ fontSize: 11, color: 'var(--ink-soft)', marginTop: 2 }}>
                          {d.trip.start && d.trip.end ? `${window.fmtDateRange(d.trip.start, d.trip.end)} · ` : ''}{d.places.length} place{d.places.length !== 1 ? 's' : ''}
                          {d.screen === 'itinerary' && d.itinerary && d.itinerary.length > 0
                            ? <span style={{ color: 'var(--teal)' }}> · routed ✓ opens your plan</span>
                            : ' · still editing'}
                        </div>
                      </div>
                      <button
                        onClick={e => handleDeleteDraft(e, d.id)}
                        style={{ background: 'none', border: 'none', color: 'var(--ink-soft)', cursor: 'pointer', fontSize: 20, lineHeight: 1, padding: '0 4px', opacity: 0.45, flexShrink: 0 }}
                      >×</button>
                    </div>
                  ))}
                </div>
              </div>
            )}
          </div>

          {/* RIGHT: floating preview cards */}
          <div className="hero-preview">
            <div className="float-card" style={{ top: 0, left: '8%', width: 240, transform: 'rotate(-4deg)', background: 'var(--mint)' }}>
              <div className="mono" style={{ marginBottom: 6 }}>day 02 · belém</div>
              <div style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 22, letterSpacing: '-0.02em', lineHeight: 1.05 }}>
                pastéis &<br/>a museum roof
              </div>
              <div style={{ marginTop: 10, display: 'flex', gap: 4, flexWrap: 'wrap' }}>
                <span className="tag">9:00</span>
                <span className="tag">walk 6min</span>
                <span className="tag">sugar coma</span>
              </div>
            </div>

            <div className="float-card" style={{ top: 130, right: '4%', width: 220, transform: 'rotate(3deg)', background: 'var(--peach)' }}>
              <div className="mono" style={{ marginBottom: 6 }}>sunset · day 01</div>
              <div style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 20, letterSpacing: '-0.02em', lineHeight: 1.1 }}>
                miradouro da<br/>senhora do monte
              </div>
              <div className="mono" style={{ marginTop: 8, color: 'var(--ink-line)' }}>↳ 18:32 sunset · bring sweater</div>
            </div>

            <div className="float-card" style={{ top: 320, left: '14%', width: 240, transform: 'rotate(-2deg)', background: 'var(--bg)' }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
                <span style={{ width: 24, height: 24, borderRadius: '50%', background: 'var(--accent)', border: '1.5px solid var(--ink-line)', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 12 }}>4</span>
                <span className="mono">21:00 · LOCKED</span>
              </div>
              <div style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 19, letterSpacing: '-0.02em', lineHeight: 1.1 }}>bairro do avillez</div>
              <div style={{ fontSize: 13, color: 'var(--ink-soft)', marginTop: 6, lineHeight: 1.4 }}>booked in june, don't be late xx</div>
            </div>

            <div className="float-card" style={{ top: 440, right: '8%', width: 200, transform: 'rotate(5deg)', background: 'var(--butter)', padding: '10px 14px' }}>
              <div className="mono" style={{ marginBottom: 4 }}>walk · 6 min</div>
              <div style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 16, letterSpacing: '-0.015em' }}>↳ 500m mostly downhill 🙏</div>
            </div>

            {/* sticker overlays */}
            <span className="sticker coral" style={{ position: 'absolute', top: 80, right: '30%', transform: 'rotate(-12deg)', zIndex: 4 }}>auto-routed ✦</span>
            <Star size={28} color="var(--accent)" style={{ position: 'absolute', top: 260, right: 40 }} />
            <Star size={16} color="var(--ink-line)" style={{ position: 'absolute', top: 380, left: 20 }} />
          </div>
        </div>

        {/* features */}
        <div className="features">
          <div className="feature f1">
            <div className="mono" style={{ marginBottom: 8 }}>step 01</div>
            <div className="num">paste.</div>
            <h3>dump your saves</h3>
            <p>google maps links, tiktok screenshots, the napkin from that brunch. we pull the address, opening hours, and the vibe.</p>
          </div>
          <div className="feature f2">
            <div className="mono" style={{ marginBottom: 8 }}>step 02</div>
            <div className="num">pin.</div>
            <h3>lock the non-negotiables</h3>
            <p>that 9pm reservation. the concert at 8. anchors stay anchored — everything else rearranges around them.</p>
          </div>
          <div className="feature f3" style={{ background: 'var(--ink-line)', color: 'var(--bg)' }}>
            <div className="mono" style={{ marginBottom: 8, color: 'var(--peach)' }}>step 03</div>
            <div className="num" style={{ color: 'var(--bg)' }}>plot.</div>
            <h3 style={{ color: 'var(--bg)' }}>get a real itinerary</h3>
            <p style={{ color: 'rgba(250,244,236,0.7)' }}>day-by-day, walking times, neighborhood clusters. drag to rearrange. share with the group chat.</p>
          </div>
        </div>

        {/* social proof / chaotic strip */}
        <div style={{ marginTop: 96, display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: 16 }}>
          <div className="metric-card">
            <span className="num" style={{ color: 'var(--coral)' }}>2,341</span>
            <div>
              <div className="mono">trips planned</div>
              <div style={{ fontSize: 13, color: 'var(--ink-soft)' }}>since tuesday</div>
            </div>
          </div>
          <div className="metric-card">
            <span className="num" style={{ color: 'var(--teal)' }}>0</span>
            <div>
              <div className="mono">subscriptions</div>
              <div style={{ fontSize: 13, color: 'var(--ink-soft)' }}>never. promise.</div>
            </div>
          </div>
          <div className="metric-card">
            <span className="num" style={{ color: 'var(--pink)' }}>∞</span>
            <div>
              <div className="mono">bookmarks accepted</div>
              <div style={{ fontSize: 13, color: 'var(--ink-soft)' }}>(go nuts)</div>
            </div>
          </div>
          <div className="metric-card" style={{ background: 'var(--peach)' }}>
            <div style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 16, letterSpacing: '-0.015em', lineHeight: 1.2 }}>
              "i found the bar.<br/>i wasn't lost."
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

// ============================================================
// INPUT
// ============================================================
function InputScreen({ onContinue, places, setPlaces, trip, setTrip }) {
  const [pasteText, setPasteText] = useState_S('');
  const [cityInput, setCityInput] = useState_S(trip.where || '');
  const [citySuggestions, setCitySuggestions] = useState_S([]);
  const [cityOpen, setCityOpen] = useState_S(false);
  const cityDebounceRef = useRef_S(null);
  const [cityLocation, setCityLocation] = useState_S(null); // { lat, lng } stored when city is picked

  const [stayInput, setStayInput] = useState_S(trip.stay || '');
  const [staySuggestions, setStaySuggestions] = useState_S([]);
  const [stayOpen, setStayOpen] = useState_S(false);
  const stayDebounceRef = useRef_S(null);

  const handleCityInput = (val) => {
    setCityInput(val);
    setTrip(prev => ({ ...prev, where: val }));
    clearTimeout(cityDebounceRef.current);
    if (val.length < 2) { setCitySuggestions([]); setCityOpen(false); return; }
    cityDebounceRef.current = setTimeout(async () => {
      try {
        const { suggestions } = await google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions({
          input: val,
          includedPrimaryTypes: ['locality', 'administrative_area_level_1', 'country'],
        });
        setCitySuggestions(suggestions.map(s => s.placePrediction));
        setCityOpen(true);
      } catch(e) { setCitySuggestions([]); }
    }, 300);
  };

  const selectCity = async (pred) => {
    try {
      const place = pred.toPlace();
      await place.fetchFields({ fields: ['displayName', 'formattedAddress', 'location'] });
      const val = place.formattedAddress || place.displayName || pred.text.text;
      setCityInput(val);
      setTrip(prev => ({ ...prev, where: val }));
      if (place.location) setCityLocation({ lat: place.location.lat(), lng: place.location.lng() });
    } catch(e) {
      const val = pred.text ? pred.text.text : String(pred);
      setCityInput(val);
      setTrip(prev => ({ ...prev, where: val }));
    }
    setCitySuggestions([]);
    setCityOpen(false);
  };

  const handleStayInput = (val) => {
    setStayInput(val);
    setTrip(prev => ({ ...prev, stay: val }));
    // If it looks like a URL, skip autocomplete — will be resolved during brewing
    if (val.startsWith('http')) { setStaySuggestions([]); setStayOpen(false); return; }
    clearTimeout(stayDebounceRef.current);
    if (val.length < 2) { setStaySuggestions([]); setStayOpen(false); return; }
    stayDebounceRef.current = setTimeout(async () => {
      try {
        const opts = { input: val };
        if (cityLocation) opts.locationBias = { center: cityLocation, radius: 50000 };
        const { suggestions } = await google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions(opts);
        // filter(Boolean) — queryPrediction entries have placePrediction=undefined and would crash the render
        setStaySuggestions(suggestions.map(s => s.placePrediction).filter(Boolean));
        setStayOpen(true);
      } catch(e) {
        console.warn('Stay autocomplete error:', e);
        setStaySuggestions([]);
      }
    }, 300);
  };

  const selectStay = async (pred) => {
    try {
      const place = pred.toPlace();
      await place.fetchFields({ fields: ['displayName', 'formattedAddress'] });
      const val = place.formattedAddress || place.displayName || pred.text.text;
      setStayInput(val);
      setTrip(prev => ({ ...prev, stay: val }));
    } catch(e) {
      const val = pred.text ? pred.text.text : String(pred);
      setStayInput(val);
      setTrip(prev => ({ ...prev, stay: val }));
    }
    setStaySuggestions([]);
    setStayOpen(false);
  };

  const categorySamples = [
    { label: 'coffee shop', query: 'specialty coffee' },
    { label: 'viewpoint', query: 'best viewpoint' },
    { label: 'local market', query: 'local market' },
    { label: 'museum', query: 'top museum' },
    { label: 'cocktail bar', query: 'cocktail bar' },
    { label: 'restaurant', query: 'local restaurant' },
  ];


  const [dupNote, setDupNote] = useState_S('');

  // Fetches the real place name for a short link in the background — the form never blocks.
  // The expanded url info is kept on the place so brewing doesn't redo the work.
  const lookupName = async (p) => {
    try {
      const { url, titleName, dead } = await window.expandShortLink(p.raw);
      const info = url ? window.extractFromMapsUrl(url) : (titleName ? { name: titleName } : null);
      setPlaces(prev => prev.map(x => x.id === p.id
        ? dead ? { ...x, name: x.hintName || '⚠ dead link', deadLink: true, pendingName: false }
        : { ...x, name: (info && info.name) || x.hintName || (info && info.lat != null ? '📍 dropped pin' : '📍 saved link'), urlInfo: info || undefined, pendingName: false }
        : x));
    } catch (e) {
      setPlaces(prev => prev.map(x => x.id === p.id ? { ...x, name: x.hintName || '📍 saved link', pendingName: false } : x));
    }
  };

  // Instantly stage raw links/names — full resolution still happens during brewing
  const addRaw = () => {
    const lines = pasteText.split('\n').map(l => l.trim()).filter(Boolean);
    if (!lines.length) return;
    const seen = new Set(places.map(p => (p.raw || '').trim()).filter(Boolean));
    let skipped = 0;
    const toAdd = [];
    lines.forEach((rawLine, i) => {
      // A link anywhere in the line is the save; any text around it is just her label for it
      // ("Café Mugrabi – https://maps.app.goo.gl/…", "[https://…](https://…)" from notes apps)
      const urlMatch = rawLine.match(/https?:\/\/[^\s)\]>'"]+/);
      const url = urlMatch ? urlMatch[0].replace(/[.,;:!?]+$/, '') : null;
      const line = url || rawLine;
      const hintName = url
        ? rawLine.replace(/https?:\/\/[^\s)\]>'"]+/g, ' ').replace(/[\[\]()<>|•·]+|[-–—:]+\s*$|^\s*[-–—:]+/g, ' ').replace(/\s+/g, ' ').trim()
        : '';
      if (seen.has(line)) { skipped++; return; }
      seen.add(line);
      // Try to get a display name without any API call
      let name = line;
      let pendingName = false;
      let urlInfo;
      if (line.startsWith('http')) {
        if (window.isShortMapsLink(line)) { name = hintName || '📍 finding name…'; pendingName = true; }
        else {
          // A long link already says what it is — name (and pin, if it has one) straight from the URL
          urlInfo = window.extractFromMapsUrl(line) || undefined;
          name = (urlInfo && urlInfo.name) || hintName
            || (urlInfo && urlInfo.lat != null ? '📍 dropped pin' : line.replace(/^https?:\/\/(www\.)?/, '').slice(0, 50));
        }
      }
      toAdd.push({ id: `raw-${Date.now()}-${i}`, name, raw: line, resolved: false, pendingName, neigh: '', tags: [], ...(hintName ? { hintName } : {}), ...(urlInfo ? { urlInfo } : {}) });
    });
    setPlaces(prev => [...prev, ...toAdd]);
    setPasteText('');
    if (skipped > 0) {
      setDupNote(`skipped ${skipped} you already added`);
      setTimeout(() => setDupNote(''), 5000);
    }
    toAdd.filter(p => p.pendingName).forEach(p => lookupName(p));
  };

  // Two chips resolving to the same place name = probably pasted twice
  const nameCounts = {};
  places.forEach(p => {
    const k = (p.name || '').trim().toLowerCase();
    if (k && !p.pendingName && !k.startsWith('📍') && !k.startsWith('⚠')) nameCounts[k] = (nameCounts[k] || 0) + 1;
  });

  // Category chips: still stage as unresolved with a search term
  const addCategory = (query) => {
    if (!trip.where) return;
    const city = trip.where.split(',')[0].trim();
    setPlaces(prev => [...prev, {
      id: `cat-${Date.now()}`,
      name: `${query} · ${city}`,
      raw: `${query} in ${city}`,
      resolved: false,
      neigh: city,
      tags: [query],
    }]);
  };

  const removePlace = (id) => setPlaces(places.filter(p => p.id !== id));

  const updateEvent = (i, field, val) => {
    const next = [...trip.events];
    next[i] = { ...next[i], [field]: val };
    setTrip({ ...trip, events: next });
  };
  const addEvent = () => setTrip({ ...trip, events: [...(trip.events || []), { name: '', day: '', time: '' }] });
  const removeEvent = (i) => setTrip({ ...trip, events: trip.events.filter((_, j) => j !== i) });

  const datesOk = !!(trip.start && trip.end && trip.end >= trip.start);
  const canContinue = trip.where && datesOk && (places.length > 0 || pasteText.trim().length > 0);
  const tripDays = datesOk ? Math.round((new Date(trip.end + 'T00:00:00') - new Date(trip.start + 'T00:00:00')) / 86400000) + 1 : 0;
  const perDay = tripDays > 0 ? places.length / tripDays : 0;

  return (
    <div className="input-screen" data-screen-label="02 Input">
      <div className="page" style={{ maxWidth: 920 }}>
        <div className="input-h">
          <div>
            <h1>tell us <span className="accent">everything.</span></h1>
            <p className="mono" style={{ marginTop: 10 }}>* but not too much. we'll do the math.</p>
          </div>
          <span className="sticker butter" style={{ transform: 'rotate(-4deg)' }}>the only form ✦</span>
        </div>

        {/* Q1: WHERE */}
        <div className="q-block">
          <span className="q-num">01 / where</span>
          <h2>where are you going?</h2>
          <p className="q-hint">city or region. we figure out the rest.</p>
          <div className="city-ac-wrap">
            <input
              className="city-text-input"
              placeholder="berlin, germany"
              value={cityInput}
              onChange={e => handleCityInput(e.target.value)}
              onBlur={() => setTimeout(() => setCityOpen(false), 150)}
              onFocus={() => citySuggestions.length > 0 && setCityOpen(true)}
              autoComplete="off"
            />
            {cityOpen && citySuggestions.length > 0 && (
              <div className="city-dropdown">
                {citySuggestions.map((pred, i) => (
                  <div key={i} className="city-dropdown-item" onMouseDown={() => selectCity(pred)}>
                    {pred.text ? pred.text.text : ''}
                  </div>
                ))}
              </div>
            )}
          </div>
        </div>

        {/* Q2: STAY */}
        <div className="q-block">
          <span className="q-num">02 / stay</span>
          <h2>where are you staying?</h2>
          <p className="q-hint">hotel name, airbnb address, or paste a google maps link. we route each day from here.</p>
          <div className="city-ac-wrap">
            <input
              className="city-text-input"
              placeholder={trip.where ? `hotel or airbnb in ${trip.where.split(',')[0].toLowerCase()}` : 'hotel name, address, or maps link'}
              value={stayInput}
              onChange={e => handleStayInput(e.target.value)}
              onBlur={() => setTimeout(() => setStayOpen(false), 150)}
              onFocus={() => staySuggestions.length > 0 && setStayOpen(true)}
              autoComplete="off"
            />
            {stayOpen && staySuggestions.length > 0 && (
              <div className="city-dropdown">
                {staySuggestions.map((pred, i) => (
                  <div key={i} className="city-dropdown-item" onMouseDown={() => selectStay(pred)}>
                    {pred.text ? pred.text.text : ''}
                  </div>
                ))}
              </div>
            )}
          </div>
          {stayInput && stayInput.startsWith('http') && (
            <p className="q-hint" style={{ marginTop: 8, color: 'var(--ink-soft)' }}>↳ link detected — will resolve when you route</p>
          )}
        </div>

        {/* Q3: WHEN */}
        <div className="q-block">
          <span className="q-num">03 / when</span>
          <h2>when's the trip?</h2>
          <p className="q-hint">we'll grab sunsets, opening hours, and check if your dates dodge any saints' days.</p>
          <div className="dates-row">
            <div className="field-group">
              <label>arrive</label>
              <input className="field-input" type="date" value={trip.start} onChange={(e) => setTrip({ ...trip, start: e.target.value })} />
            </div>
            <div className="field-group">
              <label>leave</label>
              <input className="field-input" type="date" min={trip.start || undefined} value={trip.end} onChange={(e) => setTrip({ ...trip, end: e.target.value })} />
            </div>
            <span className="sticker mint" style={{ alignSelf: 'center', marginBottom: 8 }}>
              {(() => {
                if (!trip.start || !trip.end) return "0 days";
                const d = Math.round((new Date(trip.end) - new Date(trip.start)) / 86400000) + 1;
                return d > 0 ? `${d} days · ${d-1} nights` : "leave is before arrive";
              })()}
            </span>
          </div>
        </div>

        {/* Q4: WHAT YOU SAVED */}
        <div className="q-block">
          <span className="q-num">04 / saves</span>
          <h2>what did you save?</h2>
          <p className="q-hint">paste google maps links (one per line), or just type names. we'll fetch the rest.</p>

          <textarea
            className="field-input"
            rows={4}
            placeholder={"paste any google maps link — short or long, one per line\nor just type names: Clärchens Ballhaus, Park Bar, that rooftop in mitte"}
            value={pasteText}
            onChange={(e) => setPasteText(e.target.value)}
            style={{ resize: 'vertical', fontFamily: 'var(--font-mono)', fontSize: 13 }}
          />
          {/* "or find in [city]" category chip buttons removed — Dip removed them, no current idea for what to replace with */}
          <div style={{ display: 'flex', gap: 10, marginTop: 12, alignItems: 'center', flexWrap: 'wrap' }}>
            <button
              className="btn sm dark"
              onClick={addRaw}
              disabled={!pasteText.trim()}
              style={{ opacity: !pasteText.trim() ? 0.6 : 1 }}
            >+ add saves</button>
          </div>

          {places.length > 0 && (
            <>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginTop: 20 }}>
                <span className="mono">
                  saved · {places.length} place{places.length !== 1 ? 's' : ''} · details resolve when you route
                  {dupNote && <span style={{ color: 'var(--coral)' }}> · {dupNote}</span>}
                  {perDay > 6 && <span style={{ color: 'var(--coral)' }}> · that's ~{Math.round(perDay)} a day — ambitious. add days or trim?</span>}
                </span>
                <button className="btn ghost sm" onClick={() => setPlaces([])}>clear all</button>
              </div>
              <div className="places-grid">
                {places.map(p => (
                  <div key={p.id} className={`place-chip ${p.resolved ? '' : 'pending'}`}>
                    <button className="x" onClick={() => removePlace(p.id)}>×</button>
                    <span className="name">{p.name}</span>
                    {p.resolved
                      ? <span className="neigh">↳ {p.neigh || p.address || ''}</span>
                      : <span className="neigh" style={{ opacity: 0.55 }}>{p.pendingName ? '↳ reading your link…' : p.deadLink ? "↳ this link doesn't open anymore — remove it or type the name" : '↳ details on route'}</span>
                    }
                    <div className="tags">
                      {(p.tags || []).map((t, j) => <span key={j} className="tag">{t}</span>)}
                      {nameCounts[(p.name || '').trim().toLowerCase()] > 1 && (
                        <span className="tag" style={{ background: 'var(--coral)', borderColor: 'var(--coral)', color: '#fff', fontWeight: 700 }}>pasted twice?</span>
                      )}
                    </div>
                  </div>
                ))}
              </div>
            </>
          )}
        </div>

        {/* ANCHORS REMOVED from input screen — Dip wants it after places are resolved, not before routing */}

        {/* CONTINUE */}
        <div className="input-actions">
          <span className="mono">
            {canContinue ? "✓ looks good. send it." : (trip.start && trip.end && !datesOk ? "leave date is before arrive — fix the dates" : "fill in the blanks above")}
          </span>
          <button
            className="btn primary"
            disabled={!canContinue}
            style={{ opacity: canContinue ? 1 : 0.45 }}
            onClick={() => { if (pasteText.trim()) addRaw(); onContinue(); }}
          >
            route my chaos →
          </button>
        </div>
      </div>
    </div>
  );
}

// ============================================================
// BREWING (loading transition)
// ============================================================
function BrewingScreen({ task, onDone }) {
  const messages = [
    "following your links...",
    "geocoding saves...",
    "calculating walking times...",
    "clustering by neighborhood...",
    "honoring your anchors...",
    "smoothing out the chaos...",
  ];
  const [step, setStep] = useState_S(0);
  const [progress, setProgress] = useState_S(null);

  // Run the real work; stay on this screen until it's actually done (min 2.6s so the animation lands)
  useEffect_S(() => {
    let alive = true;
    const minTime = new Promise(r => setTimeout(r, 2600));
    const work = Promise.resolve()
      .then(() => task((done, total) => { if (alive) setProgress({ done, total }); }))
      .catch(e => console.error('Brewing task failed:', e));
    Promise.all([work, minTime]).then(() => { if (alive) onDone(); });
    return () => { alive = false; };
  }, []);

  // Cycle the step messages for as long as the work runs
  useEffect_S(() => {
    const t = setInterval(() => setStep(s => s + 1), 850);
    return () => clearInterval(t);
  }, []);

  const msgIdx = step % messages.length;

  return (
    <div className="brewing" data-screen-label="03 Brewing">
      <div className="mono">brewing your itinerary</div>
      <div className="blob">
        <span className="a">r</span>
        <span className="a" style={{ animationDelay: '.1s' }}>o</span>
        <span className="a" style={{ animationDelay: '.2s' }}>u</span>
        <span className="a" style={{ animationDelay: '.3s' }}>t</span>
        <span className="a" style={{ animationDelay: '.4s' }}>i</span>
        <span className="a" style={{ animationDelay: '.5s' }}>n</span>
        <span className="a" style={{ animationDelay: '.6s' }}>g</span>
        <span className="a" style={{ animationDelay: '.7s' }}>.</span>
        <span className="a" style={{ animationDelay: '.8s' }}>.</span>
        <span className="a" style={{ animationDelay: '.9s' }}>.</span>
      </div>
      <div className="step">{messages[msgIdx]}</div>
      {progress && progress.total > 0 && (
        <div className="mono" style={{ marginTop: 8, color: 'var(--ink-soft)' }}>
          found {progress.done} of {progress.total} places
        </div>
      )}
      <div style={{ display: 'flex', gap: 6, marginTop: 12 }}>
        {messages.map((_, i) => (
          <span key={i} style={{
            width: 24, height: 6, borderRadius: 3,
            background: (progress && progress.total > 0
              ? i < Math.round((progress.done / progress.total) * messages.length)
              : i <= msgIdx) ? 'var(--accent)' : 'var(--bg-2)',
            border: '1.5px solid var(--ink-line)',
          }} />
        ))}
      </div>
    </div>
  );
}

Object.assign(window, { LandingScreen, InputScreen, BrewingScreen });
