/* global React, ReactDOM, window */
const {
  TapedPhoto, Sketch, FieldLine, LogoGoat, GoatOrnament, SignOff, NOTES,
  CATEGORIES, IcoLedger, IcoPencil, IcoFountainPen, IcoEnvelope,
  IcoTypewriter, IcoBook, IcoPin, IcoLamp, IcoX, IcoTrash, IcoCheck, IcoLocation, IcoChevron,
  LGStore, useLGStore, getBrowserLocation,
  TAGS, PROMPTS, dailyPromptIndex
} = window;

/*
 * Field Desk — single-page notebook.
 *
 * TOP        :  Pinned slot (one item — anything you pin lives here)
 * BELOW      :  Four colored folder tabs (Entries · Drafts · Sketches · Letters)
 * MAIN       :  ONE blank notebook page. Header changes per mode.
 * BOTTOM     :  Menu bar with the four mode tabs (Write · Read · Sketch · Letter)
 * RIGHT      :  Folder tray slides in when a folder is opened
 *
 * Per-mode header (letterhead):
 *   Write  → "Goat Note" date stamp + title + location
 *   Read   → entry title + meta
 *   Sketch → "Pen on paper" date stamp + title
 *   Letter → "A letter, sent" date stamp + To/Subject
 *
 * Storage : LGStore (entries / drafts / sketches / letters / pinned)
 */

/*
 * Daily prompt — picks one from PROMPTS deterministically by date,
 * but the user can refresh to draw a new one (offset stored locally).
 */
function getDailyPrompt(offset) {
  return PROMPTS[dailyPromptIndex(offset)];
}
function todayStamp() {
  const d = new Date();
  const m = ["JAN","FEB","MAR","APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC"][d.getMonth()];
  return `${m} · ${String(d.getDate()).padStart(2,"0")} · ${d.getFullYear()}`;
}

/* =============================================================
 * DAILY PROMPT CARD — stuck to the desk, refreshable
 * ============================================================= */
function DailyPromptCard({ onUse }) {
  const [offset, setOffset] = React.useState(() => {
    try { return parseInt(localStorage.getItem("lg.promptOffset") || "0", 10); }
    catch (_) { return 0; }
  });
  const refresh = () => {
    const next = offset + 1;
    setOffset(next);
    try { localStorage.setItem("lg.promptOffset", String(next)); } catch (_) {}
  };
  const prompt = getDailyPrompt(offset);
  return (
    <aside className="prompt-card" aria-label="Today's prompt">
      <div className="prompt-card__head">
        <span className="eyebrow">Today's prompt</span>
        <span className="prompt-card__date">{todayStamp()}</span>
      </div>
      <p className="prompt-card__text">{prompt}</p>
      <div className="prompt-card__foot">
        <button className="btn btn--ghost btn--sm" onClick={refresh} title="Pull another">
          <IcoChevron size={14} dir="right" /> <span style={{marginLeft:6}}>Pull another</span>
        </button>
        <button className="btn btn--sm" onClick={() => onUse(prompt)}>Use this</button>
      </div>
      <span className="prompt-card__corner" aria-hidden="true" />
    </aside>
  );
}

/* =============================================================
 * TAG PICKER — chip rail with searchable popover
 * Used inline in the letterhead for write/sketch/letter.
 *
 * The popover is portaled to document.body so it escapes any
 * `overflow: hidden` ancestor (the .desk container clips its
 * children). Position is computed from the picker's bounding rect
 * and re-computed on scroll/resize while open.
 * ============================================================= */
function TagPicker({ value = [], onChange, max = 4 }) {
  const [open, setOpen] = React.useState(false);
  const [q, setQ] = React.useState("");
  const wrapRef = React.useRef(null);
  const popRef = React.useRef(null);
  const [pos, setPos] = React.useState({ top: 0, left: 0, width: 320 });

  // Read TAGS at render time (window is the source of truth) so we don't
  // depend on a destructured value possibly being stale.
  const TAGS_LIST = (typeof window !== 'undefined' && window.TAGS) || [];

  const placePop = React.useCallback(() => {
    const el = wrapRef.current;
    if (!el) return;
    const r = el.getBoundingClientRect();
    setPos({ top: r.bottom + 6, left: r.left, width: Math.max(280, r.width) });
  }, []);

  React.useEffect(() => {
    if (!open) return;
    placePop();
    const onDown = (e) => {
      const t = e.target;
      if (popRef.current && popRef.current.contains(t)) return;
      if (wrapRef.current && wrapRef.current.contains(t)) return;
      setOpen(false);
    };
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('pointerdown', onDown, true);
    document.addEventListener('keydown', onKey);
    window.addEventListener('scroll', placePop, true);
    window.addEventListener('resize', placePop);
    return () => {
      document.removeEventListener('pointerdown', onDown, true);
      document.removeEventListener('keydown', onKey);
      window.removeEventListener('scroll', placePop, true);
      window.removeEventListener('resize', placePop);
    };
  }, [open, placePop]);

  const toggle = (id) => {
    if (value.includes(id)) onChange(value.filter(t => t !== id));
    else if (value.length < max) onChange([...value, id]);
  };

  const filtered = TAGS_LIST.filter(t => t.label.toLowerCase().includes(q.toLowerCase()));

  const popover = open && (
    <div
      ref={popRef}
      className="tagpick__pop tagpick__pop--portal"
      style={{ position: 'fixed', top: pos.top, left: pos.left, width: pos.width, zIndex: 9999 }}
      role="dialog"
      aria-label="Pick tags"
    >
      <input
        className="tagpick__search"
        autoFocus
        placeholder="Filter tags…"
        value={q}
        onChange={e => setQ(e.target.value)}
      />
      <div className="tagpick__grid">
        {filtered.map(t => (
          <button
            key={t.id}
            type="button"
            className={"tagchip" + (value.includes(t.id) ? " tagchip--on" : "")}
            style={{ '--leather': t.leather }}
            onClick={() => toggle(t.id)}
          >
            {t.label}
            {value.includes(t.id) && <IcoCheck size={10} />}
          </button>
        ))}
        {filtered.length === 0 && (
          <span className="tagpick__empty" style={{ padding: '8px 4px' }}>no matches</span>
        )}
      </div>
      <div className="tagpick__hint serif">
        Pick up to {max}. These file the entry under those folders on Goat Notes.
      </div>
    </div>
  );

  return (
    <div className="tagpick" ref={wrapRef}>
      <div className="tagpick__row">
        <span className="tagpick__label">Tags</span>
        <div className="tagpick__chips">
          {value.length === 0 && <span className="tagpick__empty">none yet</span>}
          {value.map(id => {
            const t = TAGS_LIST.find(x => x.id === id);
            if (!t) return null;
            return (
              <button
                key={id}
                type="button"
                className="tagchip tagchip--on"
                style={{ '--leather': t.leather }}
                onClick={() => toggle(id)}
                title="Remove"
              >
                {t.label}
                <IcoX size={10} />
              </button>
            );
          })}
          <button
            type="button"
            className="tagpick__add"
            onClick={() => setOpen(v => !v)}
            disabled={value.length >= max}
            aria-expanded={open}
          >
            + Add tag
          </button>
        </div>
      </div>
      {popover && ReactDOM.createPortal(popover, document.body)}
    </div>
  );
}

/* =============================================================
 * PINNED SLOT — anything can live here
 * ============================================================= */
function PinnedSlot({ pinned, onClear, onPick }) {
  if (!pinned) {
    return (
      <div className="pinned pinned--empty">
        <div className="pinned__pin"><IcoPin size={18} /></div>
        <div className="pinned__placeholder">
          <span className="hand" style={{ fontSize: 22, color: 'var(--ink-soft)' }}>Nothing pinned yet.</span>
          <span className="serif pinned__hint">
            Open any folder and tap the pin to bring an entry, draft, sketch, or letter to the top of the desk.
          </span>
        </div>
        <button className="btn btn--ghost btn--sm" onClick={onPick}>Pick something</button>
      </div>
    );
  }
  const cat = CATEGORIES[pinned.kind];
  const Icon = cat?.Icon || IcoLedger;
  const isSketch = pinned.kind === "sketches";
  return (
    <div className="pinned" style={{ '--cat-color': cat?.color || 'var(--gold)' }}>
      <div className="pinned__pin"><IcoPin size={18} /></div>
      <div className="pinned__chip">
        <Icon size={14} color={cat?.color} stroke={1.6} />
        <span>{cat?.short}</span>
      </div>
      <div className="pinned__body">
        <span className="hand pinned__title">{pinned.title || "Untitled"}</span>
        {pinned.preview && !isSketch && (
          <p className="pinned__preview serif">{pinned.preview}</p>
        )}
        {(pinned.location || pinned.date) && (
          <span className="pinned__meta">
            {pinned.location ? pinned.location : ""}
            {pinned.location && pinned.date ? " · " : ""}
            {pinned.date || ""}
          </span>
        )}
      </div>
      {isSketch && pinned.png && (
        <div className="pinned__thumb">
          <img src={pinned.png} alt={pinned.title || "Sketch"} />
        </div>
      )}
      <button className="pinned__close" onClick={onClear} aria-label="Unpin"><IcoX size={16} /></button>
    </div>
  );
}

/* =============================================================
 * FOLDER TABS — colored manila folders across the top
 * ============================================================= */
function FolderTabs({ open, setOpen, counts }) {
  return (
    <div className="folders" role="tablist">
      {Object.values(CATEGORIES).map(cat => {
        const Icon = cat.Icon;
        const isOpen = open === cat.id;
        return (
          <button
            key={cat.id}
            className={"folder" + (isOpen ? " folder--open" : "")}
            onClick={() => setOpen(isOpen ? null : cat.id)}
            style={{ '--cat-color': cat.color }}
            role="tab"
            aria-selected={isOpen}
          >
            <span className="folder__nub" />
            <span className="folder__row">
              <span className="folder__icon"><Icon size={20} color={cat.color} stroke={1.6} /></span>
              <span className="folder__label">{cat.label}</span>
              <span className="folder__count">{counts[cat.id] || 0}</span>
            </span>
          </button>
        );
      })}
    </div>
  );
}

/* =============================================================
 * FOLDER TRAY — content of an open folder
 * ============================================================= */
function FolderTray({ id, onClose, onPick, onPin, onEdit, onDelete }) {
  const cat = CATEGORIES[id];
  const items = LGStore.list(id);
  const seeded = id === "entries"
    ? NOTES.map(n => ({
        id: "seed-" + n.id,
        kind: "entries",
        seed: true,
        title: n.title,
        date: n.date,
        location: n.location,
        coords: n.coords,
        preview: n.excerpt,
        body: n.excerpt
      }))
    : [];
  const all = [...items, ...seeded];

  return (
    <aside className="tray" role="tabpanel" style={{ '--cat-color': cat.color }}>
      <header className="tray__head">
        <div className="tray__title">
          <cat.Icon size={20} color={cat.color} />
          <span className="eyebrow">{cat.label}</span>
        </div>
        <button className="tray__close" onClick={onClose} aria-label="Close folder"><IcoX size={18} /></button>
      </header>
      <div className="tray__hint serif">{cat.hint}</div>

      <div className={"tray__body" + (id === "sketches" ? " tray__body--grid" : "")}>
        {all.length === 0 && (
          <div className="tray__empty">
            <span className="hand" style={{ fontSize: 26, color: 'var(--ink-soft)' }}>nothing here yet.</span>
            <p className="serif" style={{ fontStyle: 'italic', color: 'var(--ink-faint)' }}>
              {id === "entries" && "File a Goat Note from the journal and it'll land here."}
              {id === "drafts" && "Working notes auto-save here while you write."}
              {id === "sketches" && "Doodle in the journal and save the page here."}
              {id === "letters" && "Letters you've sent (not blog posts) live here."}
            </p>
          </div>
        )}
        {all.map(item => (
          <article key={item.id} className={"tray__item" + (id === "sketches" ? " tray__item--sketch" : "")}>
            {id === "sketches" && (
              <div className="tray__sketch-frame">
                {item.png ? (
                  <img src={item.png} alt={item.title || "Sketch"} />
                ) : (
                  <Sketch kind="mountains" height={90} />
                )}
              </div>
            )}
            <div className="tray__item-head">
              <span className="tray__item-title hand">{item.title || "Untitled"}</span>
              {!item.seed && (
                <button className="tray__icon-btn" onClick={() => onDelete(id, item.id)} title="Discard"><IcoTrash size={14} /></button>
              )}
            </div>
            {item.preview && id !== "sketches" && <p className="tray__item-preview serif">{item.preview}</p>}
            {(item.location || item.date) && (
              <span className="tray__item-meta">
                {item.location || ""}{item.location && item.date ? " · " : ""}{item.date || ""}
              </span>
            )}
            {item.tags && item.tags.length > 0 && (
              <div className="tray__item-tags">
                {item.tags.slice(0, 3).map(tid => {
                  const t = TAGS.find(x => x.id === tid);
                  if (!t) return null;
                  return (
                    <span key={tid} className="tagchip tagchip--tiny" style={{ '--leather': t.leather }}>
                      {t.label}
                    </span>
                  );
                })}
              </div>
            )}
            <div className="tray__actions">
              <button className="btn btn--ghost btn--sm" onClick={() => onPick(item)}>
                {id === "entries" || id === "drafts" ? "Open" : id === "letters" ? "Read" : "View"}
              </button>
              {!item.seed && (
                <button className="btn btn--ghost btn--sm" onClick={() => onEdit(item)} title="Edit">
                  <IcoPencil size={14} /> <span style={{marginLeft:4}}>Edit</span>
                </button>
              )}
              <button className="btn btn--ghost btn--sm" onClick={() => onPin(item)}>
                <IcoPin size={14} /> <span style={{marginLeft:4}}>Pin</span>
              </button>
            </div>
          </article>
        ))}
      </div>
    </aside>
  );
}

/* =============================================================
 * BOTTOM MENU BAR — Write · Read · Sketch · Letter
 * ============================================================= */
const TABS = [
  { id: "write",  label: "Write",  Icon: IcoTypewriter, hint: "A field note" },
  { id: "read",   label: "Read",   Icon: IcoBook,       hint: "Past entries" },
  { id: "sketch", label: "Sketch", Icon: IcoFountainPen, hint: "Pen on paper" },
  { id: "letter", label: "Letter", Icon: IcoEnvelope,   hint: "Send to someone" },
];
function MenuBar({ mode, setMode }) {
  return (
    <nav className="menubar" role="tablist" aria-label="Notebook mode">
      {TABS.map(t => {
        const Icon = t.Icon;
        const on = mode === t.id;
        return (
          <button
            key={t.id}
            role="tab"
            aria-selected={on}
            className={"menubar__btn" + (on ? " menubar__btn--on" : "")}
            onClick={() => setMode(t.id)}
          >
            <Icon size={22} stroke={1.6} />
            <span className="menubar__label">{t.label}</span>
            <span className="menubar__hint">{t.hint}</span>
          </button>
        );
      })}
    </nav>
  );
}

/* =============================================================
 * SAVE-AS picker
 * ============================================================= */
function SaveAsBar({ onSaveEntry, onSaveDraft, onSaveLetter, canSave, kinds = ["entries","drafts","letters"] }) {
  return (
    <div className="saveas">
      <span className="eyebrow">Save as</span>
      {kinds.includes("entries") && (
        <button className="saveas__btn saveas__btn--gold" onClick={onSaveEntry} disabled={!canSave}>
          <IcoLedger size={18} color="var(--cat-gold)" />
          <span>Goat Note</span>
          <span className="saveas__hint">publish to Field Entries</span>
        </button>
      )}
      {kinds.includes("drafts") && (
        <button className="saveas__btn saveas__btn--sage" onClick={onSaveDraft} disabled={!canSave}>
          <IcoPencil size={18} color="var(--cat-sage)" />
          <span>Draft</span>
          <span className="saveas__hint">keep working on it</span>
        </button>
      )}
      {kinds.includes("letters") && (
        <button className="saveas__btn saveas__btn--wax" onClick={onSaveLetter} disabled={!canSave}>
          <IcoEnvelope size={18} color="var(--cat-wax)" />
          <span>Letter</span>
          <span className="saveas__hint">send to someone</span>
        </button>
      )}
    </div>
  );
}

/* =============================================================
 * LETTERHEAD — per-mode header that prints atop the blank page
 * ============================================================= */
const _gridRow = { display: 'grid', gridTemplateColumns: '90px 1fr', gap: 12, padding: '6px 0', borderBottom: '1px dashed var(--rule)' };

function Letterhead({ mode, setMode, draft, setDraft, note }) {
  const [geoBusy, setGeoBusy] = React.useState(false);
  const wordCount = (draft.body || "").split(/\s+/).filter(Boolean).length;

  const fetchGeo = async () => {
    setGeoBusy(true);
    const r = await getBrowserLocation();
    if (!r) { setGeoBusy(false); alert("Couldn't read your location. Type one in by hand."); return; }
    setDraft({ ...draft, coords: r.coords });
    const rg = window.reverseGeocode;
    const fw = window.fetchWeather;
    const [place, wx] = await Promise.all([
      rg ? rg(r.lat, r.lon) : Promise.resolve("n/a"),
      fw ? fw(r.lat, r.lon) : Promise.resolve("n/a")
    ]);
    setDraft({ ...draft, coords: r.coords, location: place, weather: wx });
    setGeoBusy(false);
  };

  return (
    <div className={"letterhead letterhead--" + mode}>
      <div className="letterhead__top">
        <div className="letterhead__stamp">
          <span className="letterhead__kind">
            {mode === "write" && "Goat Note"}
            {mode === "read" && (note ? "Reading" : "Reading Room")}
            {mode === "sketch" && "Pen on paper"}
            {mode === "letter" && "A letter, sent"}
          </span>
          <span className="letterhead__date">{todayStamp()}</span>
        </div>
        {/* Mode tabs — Write / Read / Sketch / Letter — moved from the
            bottom drawer so they're visible without scrolling. */}
        {setMode && (
          <nav className="letterhead__modes" role="tablist" aria-label="Notebook mode">
            {TABS.map(t => {
              const Icon = t.Icon;
              const on = mode === t.id;
              return (
                <button
                  key={t.id}
                  role="tab"
                  aria-selected={on}
                  className={"letterhead__mode" + (on ? " letterhead__mode--on" : "")}
                  onClick={() => setMode(t.id)}
                  title={t.hint}
                >
                  <Icon size={16} stroke={1.6} />
                  <span>{t.label}</span>
                </button>
              );
            })}
          </nav>
        )}
        <div className="letterhead__mark">
          <LogoGoat size={36} tone="gold" />
        </div>
      </div>

      {mode === "write" && (
        <div style={{ marginBottom: 16 }}>
          {/* Date */}
          <div style={_gridRow}>
            <span className="eyebrow">Date</span>
            <span style={{ fontFamily: 'var(--f-type)', fontSize: 16 }}>{todayStamp()}</span>
          </div>
          {/* Loc + Use my location */}
          <div style={{ ..._gridRow, alignItems: 'center' }}>
            <span className="eyebrow">Loc.</span>
            <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
              <input
                className="ritual-line"
                value={draft.location || ""}
                onChange={e => setDraft({ ...draft, location: e.target.value })}
                placeholder="auto-fills with your location, or type one"
                style={{ borderBottom: 'none', flex: 1 }}
              />
              <button onClick={fetchGeo} disabled={geoBusy} className="btn btn--ghost btn--sm" title="Use my location">
                <IcoLocation size={14} /> <span style={{ marginLeft: 4 }}>{geoBusy ? "Locating…" : "Use my location"}</span>
              </button>
            </div>
          </div>
          {/* Coord */}
          <div style={_gridRow}>
            <span className="eyebrow">Coord.</span>
            <input
              className="ritual-line"
              value={draft.coords || ""}
              onChange={e => setDraft({ ...draft, coords: e.target.value })}
              placeholder="29.2498° N, 103.2502° W"
              style={{ borderBottom: 'none', fontFamily: 'var(--f-mono)' }}
            />
          </div>
          {/* Wx */}
          <div style={_gridRow}>
            <span className="eyebrow">Wx.</span>
            <input
              className="ritual-line"
              value={draft.weather || ""}
              onChange={e => setDraft({ ...draft, weather: e.target.value })}
              placeholder="auto-fills with your location"
              style={{ borderBottom: 'none' }}
            />
          </div>
          {/* Title */}
          <div style={_gridRow}>
            <span className="eyebrow">Title</span>
            <input
              className="ritual-line"
              value={draft.title || ""}
              onChange={e => setDraft({ ...draft, title: e.target.value })}
              placeholder="what are you calling this?"
              style={{ borderBottom: 'none' }}
            />
          </div>
          {/* Words (auto, derived from body) */}
          <div style={{ ..._gridRow, alignItems: 'center' }}>
            <span className="eyebrow">Words</span>
            <span style={{ fontFamily: 'var(--f-type)', fontSize: 16, color: 'var(--ink-soft)' }}>
              {wordCount} {wordCount === 1 ? 'word' : 'words'} · auto
            </span>
          </div>
          {/* Photo (optional hero — disposable-camera vibe on the published note) */}
          <div style={{ ..._gridRow, alignItems: 'center' }}>
            <span className="eyebrow">Photo</span>
            <div style={{ display: 'flex', gap: 12, alignItems: 'center', flexWrap: 'wrap' }}>
              {draft.heroImage ? (
                <>
                  <img src={draft.heroImage} alt="" style={{ width: 60, height: 40, objectFit: 'cover', border: '1px solid var(--rule)' }} />
                  <button onClick={() => setDraft({ ...draft, heroImage: "" })} className="btn btn--ghost btn--sm">Remove</button>
                </>
              ) : (
                <label className="btn btn--ghost btn--sm" style={{ cursor: 'pointer' }}>
                  Upload a photo
                  <input
                    type="file"
                    accept="image/*"
                    style={{ display: 'none' }}
                    onChange={async (e) => {
                      const f = e.target.files && e.target.files[0];
                      if (!f) return;
                      try {
                        const url = window.processUploadedImage
                          ? await window.processUploadedImage(f)
                          : await new Promise((res, rej) => {
                              const r = new FileReader();
                              r.onload = () => res(r.result);
                              r.onerror = () => rej(new Error("Couldn't read that file."));
                              r.readAsDataURL(f);
                            });
                        setDraft({ ...draft, heroImage: url });
                      } catch (err) {
                        alert(err.message || "Couldn't use that image.");
                      }
                      e.target.value = "";
                    }}
                  />
                </label>
              )}
              <span style={{ fontFamily: 'var(--f-serif)', fontStyle: 'italic', fontSize: 13, color: 'var(--ink-faint)' }}>
                optional · disposable-camera vibe applied automatically
              </span>
            </div>
          </div>
          {/* Tags */}
          <TagPicker
            value={draft.tags || []}
            onChange={(tags) => setDraft({ ...draft, tags })}
          />
        </div>
      )}

      {mode === "sketch" && (
        <>
          <input
            className="letterhead__title"
            placeholder="A title for this sketch."
            value={draft.title}
            onChange={e => setDraft({ ...draft, title: e.target.value })}
          />
          <TagPicker
            value={draft.tags || []}
            onChange={(tags) => setDraft({ ...draft, tags })}
          />
        </>
      )}

      {mode === "letter" && (
        <div className="letterhead__lines">
          <div className="letterhead__line">
            <label>To</label>
            <input
              placeholder="Whom is this for?"
              value={draft.to || ""}
              onChange={e => setDraft({ ...draft, to: e.target.value })}
              style={{ fontFamily: 'var(--f-script)', fontSize: 24 }}
            />
          </div>
          <div className="letterhead__line">
            <label>Subject</label>
            <input
              placeholder="(optional)"
              value={draft.title}
              onChange={e => setDraft({ ...draft, title: e.target.value })}
              style={{ fontFamily: 'var(--f-display)', fontSize: 16, fontWeight: 600, letterSpacing: '0.06em', textTransform: 'uppercase' }}
            />
          </div>
          <TagPicker
            value={draft.tags || []}
            onChange={(tags) => setDraft({ ...draft, tags })}
          />
        </div>
      )}

      {mode === "read" && note && (
        <>
          <h2 className="letterhead__title-static">{note.title || "Untitled"}</h2>
          <div className="letterhead__metarow">
            {note.date && <FieldLine label="Date" value={note.date} />}
            {note.location && <FieldLine label="Loc." value={note.location} />}
            {note.coords && <FieldLine label="Coords" value={note.coords} />}
          </div>
          {note.tags && note.tags.length > 0 && (
            <div className="read-tags">
              {note.tags.map(id => {
                const t = TAGS.find(x => x.id === id);
                if (!t) return null;
                return (
                  <span key={id} className="tagchip tagchip--read" style={{ '--leather': t.leather }}>
                    {t.label}
                  </span>
                );
              })}
            </div>
          )}
        </>
      )}
    </div>
  );
}

/* =============================================================
 * READ SHELF — card list of every readable item, grouped by month.
 * Shown in the notebook's read pane when nothing is open.
 * If items exceed READ_THRESHOLD we collapse months → folders the user opens.
 * ============================================================= */
const READ_THRESHOLD = 18;

function readItemKind(item) {
  if (item.kind === "letters") return "letter";
  if (item.kind === "sketches") return "sketch";
  return "entry";
}

function readItemColor(item) {
  const k = readItemKind(item);
  return k === "letter" ? "var(--cat-wax)"
       : k === "sketch" ? "var(--cat-clay)"
       :                  "var(--cat-gold)";
}

function readItemMonthKey(item) {
  // item.date is "OCT · 25 · 2026" or "October 12, 2025"; fall back to "Older"
  const s = item.date || "";
  const m1 = s.match(/^([A-Z]{3})\s*·\s*\d+\s*·\s*(\d{4})/);
  if (m1) return m1[1] + " " + m1[2];
  const m2 = s.match(/^([A-Za-z]+)\s+\d+,\s*(\d{4})/);
  if (m2) return m2[1].slice(0, 3).toUpperCase() + " " + m2[2];
  return "Older";
}

function ReadShelf({ items, onPick }) {
  const [openMonth, setOpenMonth] = React.useState(null);
  const safe = items || [];
  if (safe.length === 0) {
    return (
      <div className="readshelf__empty">
        <span className="hand">Nothing filed yet. The shelf will fill up.</span>
      </div>
    );
  }

  // group by month
  const groups = React.useMemo(() => {
    const g = new Map();
    for (const it of safe) {
      const key = readItemMonthKey(it);
      if (!g.has(key)) g.set(key, []);
      g.get(key).push(it);
    }
    // most recent first — go by the first item's _ts if available
    return [...g.entries()].sort((a, b) => {
      const ta = a[1][0]._ts || 0, tb = b[1][0]._ts || 0;
      return tb - ta;
    });
  }, [safe]);

  const tooMany = safe.length > READ_THRESHOLD;

  return (
    <div className="readshelf">
      <div className="readshelf__head">
        <span className="eyebrow">The shelf</span>
        <span className="readshelf__head-line" />
        <span className="readshelf__count">{safe.length} on file</span>
      </div>

      {!tooMany && (
        <div className="readshelf__cards">
          {safe.map(item => (
            <ReadCard key={(item.id || item.no) + "-" + readItemKind(item)} item={item} onPick={onPick} />
          ))}
        </div>
      )}

      {tooMany && (
        <div className="readshelf__folders">
          {groups.map(([month, list]) => {
            const open = openMonth === month;
            return (
              <div key={month} className={"readshelf__group" + (open ? " readshelf__group--open" : "")}>
                <button
                  className="readshelf__folder"
                  onClick={() => setOpenMonth(open ? null : month)}
                >
                  <span className="readshelf__folder-tab" />
                  <span className="readshelf__folder-month">{month}</span>
                  <span className="readshelf__folder-count">{list.length}</span>
                  <IcoChevron size={14} dir={open ? "down" : "right"} />
                </button>
                {open && (
                  <div className="readshelf__cards">
                    {list.map(item => (
                      <ReadCard key={(item.id || item.no) + "-" + readItemKind(item)} item={item} onPick={onPick} />
                    ))}
                  </div>
                )}
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

function ReadCard({ item, onPick }) {
  const kind = readItemKind(item);
  const color = readItemColor(item);
  const dateStr = item.date || "";
  return (
    <button
      className={"readcard readcard--" + kind}
      style={{ '--card-color': color }}
      onClick={() => onPick(item)}
    >
      <span className="readcard__nub" aria-hidden="true" />
      <div className="readcard__head">
        <span className="readcard__kind">
          {kind === "letter" ? "Letter" : kind === "sketch" ? "Sketch" : item.category || "Field Note"}
        </span>
        <span className="readcard__date">{dateStr}</span>
      </div>
      <h4 className="readcard__title">{item.title || "Untitled"}</h4>
      {kind === "sketch" && item.png && (
        <span className="readcard__thumb">
          <img src={item.png} alt={item.title || "Sketch"} />
        </span>
      )}
      {kind !== "sketch" && (item.preview || item.excerpt || item.body) && (
        <p className="readcard__preview">
          {(item.preview || item.excerpt || (item.body || "").slice(0, 120) + "…").replace(/\s+/g, " ")}
        </p>
      )}
      {item.location && (
        <div className="readcard__loc">
          <IcoLocation size={11} /> <span>{item.location}</span>
        </div>
      )}
    </button>
  );
}

/* =============================================================
 * STAMP PICKER — pop a tray of sketch plates to stamp into a note
 * Inserts a marker like {{sketch:mountains}} into the draft body;
 * the reading view + tray render the sketch plate inline.
 * ============================================================= */
const STAMPABLE = [
  { id: "mountains", label: "Range" },
  { id: "truck",     label: "Truck" },
  { id: "fire",      label: "Campfire" },
  { id: "map",       label: "Route" },
  { id: "kettle",    label: "Coffee" },
  { id: "compass",   label: "Compass" },
  { id: "whiskey",   label: "Whiskey" },
  { id: "sunset",    label: "Sunset" },
  { id: "stars",     label: "Stars" },
  { id: "agave",     label: "Agave" },
  { id: "ocotillo",  label: "Ocotillo" },
  { id: "storm",     label: "Storm" },
];
function StampPicker({ onPick }) {
  const [open, setOpen] = React.useState(false);

  React.useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [open]);

  // Centered modal so all 12 plates are clearly visible at a readable size.
  const modal = open && (
    <div className="lg-stamp-modal-overlay" onClick={() => setOpen(false)}>
      <div
        className="lg-stamp-modal"
        role="dialog"
        aria-label="Pick a sketch to stamp"
        onClick={(e) => e.stopPropagation()}
      >
        <div className="lg-stamp-modal__head">
          <div>
            <span className="eyebrow">Plates</span>
            <span className="serif" style={{ fontSize: 13, color: 'var(--ink-soft)', marginLeft: 14 }}>
              Tap one to drop into your note
            </span>
          </div>
          <button className="btn btn--ghost btn--sm" onClick={() => setOpen(false)} aria-label="Close">Close</button>
        </div>
        <div className="lg-stamp-modal__grid">
          {STAMPABLE.map(s => (
            <button
              key={s.id}
              type="button"
              className="stamp-picker__plate"
              onClick={() => { onPick(s.id); setOpen(false); }}
              title={s.label}
            >
              <span className="stamp-picker__plate-art">
                <Sketch kind={s.id} height={110} />
              </span>
              <span className="stamp-picker__plate-label">{s.label}</span>
            </button>
          ))}
        </div>
      </div>
    </div>
  );

  return (
    <div className="stamp-picker">
      <button
        type="button"
        className="btn btn--ghost btn--sm stamp-picker__trigger"
        onClick={() => setOpen(true)}
        aria-haspopup="dialog"
        title="Stamp a sketch into your note"
      >
        <IcoFountainPen size={14} />
        <span style={{ marginLeft: 6 }}>Stamp a sketch</span>
      </button>
      {modal && ReactDOM.createPortal(modal, document.body)}
    </div>
  );
}

/* Render a body string with {{sketch:kind}} markers as paragraphs + plates */
function renderBodyWithStamps(body) {
  if (!body) return null;
  const parts = body.split(/(\{\{sketch:[a-z]+\}\})/g);
  return parts.map((p, i) => {
    const m = p.match(/^\{\{sketch:([a-z]+)\}\}$/);
    if (m) {
      return (
        <span key={i} className="inline-stamp">
          <Sketch kind={m[1]} height={140} />
        </span>
      );
    }
    return <span key={i}>{p}</span>;
  });
}

/* =============================================================
 * NOTEBOOK — single page; mode controls header & body
 * ============================================================= */
function Notebook({ mode, setMode, draft, setDraft, readingNote, onSave, onClear, onSaveSketch, onEditNote, readableItems, onPickItem, setReadingNote }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (!ref.current) return;
    ref.current.style.height = 'auto';
    ref.current.style.height = Math.max(360, ref.current.scrollHeight) + 'px';
  }, [draft.body, mode]);

  return (
    <div className={"notebook notebook--" + mode}>
      <div className="notebook__page">
        <Letterhead mode={mode} setMode={setMode} draft={draft} setDraft={setDraft} note={readingNote} />

        <div className="notebook__body">
          {mode === "write" && (
            <>
              <textarea
                ref={ref}
                className="notebook__write"
                placeholder="Take your time. Lonesome Goat is filed slowly.&#10;&#10;Where were you? What was the weather doing? Who was there? What broke? What didn't?"
                value={draft.body}
                onChange={e => setDraft({ ...draft, body: e.target.value })}
              />
              <div className="notebook__foot">
                <span className="notebook__count">{draft.body.length} chars · auto-saving</span>
                <StampPicker onPick={(id) => setDraft({ ...draft, body: draft.body + (draft.body && !draft.body.endsWith("\n") ? "\n\n" : "") + `{{sketch:${id}}}\n\n` })} />
                <button className="btn btn--ghost btn--sm" onClick={onClear}>Clear</button>
              </div>
              <SaveAsBar
                onSaveEntry={() => onSave("entries")}
                onSaveDraft={() => onSave("drafts")}
                onSaveLetter={() => onSave("letters")}
                canSave={!!draft.body.trim()}
              />
            </>
          )}

          {mode === "read" && (
            !readingNote ? (
              <ReadShelf items={readableItems} onPick={onPickItem} />
            ) : (
              <div className="notebook__read">
                <button
                  className="reader-back"
                  onClick={() => setReadingNote(null)}
                  title="Back to the shelf"
                >
                  <IcoChevron size={14} dir="left" />
                  <span>Back to the shelf</span>
                </button>
                <p className="notebook__prose">{renderBodyWithStamps(readingNote.body || readingNote.preview)}</p>
                <div className="reader-actions">
                  {!readingNote.seed && onEditNote && (
                    <button className="btn btn--ghost btn--sm" onClick={() => onEditNote(readingNote)}>
                      <IcoPencil size={14} /> <span style={{marginLeft:6}}>Edit</span>
                    </button>
                  )}
                  <ExportMenu entries={readingNote} label="Take it with you" title="Save this entry as a file you keep" />
                </div>
              </div>
            )
          )}

          {mode === "sketch" && (
            <SketchPad draft={draft} setDraft={setDraft} onSave={onSaveSketch} />
          )}

          {mode === "letter" && (
            <>
              <textarea
                ref={ref}
                className="notebook__write notebook__write--letter"
                placeholder="Friend, —&#10;&#10;Write the kind of letter you'd actually want to receive."
                value={draft.body}
                onChange={e => setDraft({ ...draft, body: e.target.value })}
              />
              <div className="notebook__foot">
                <span className="notebook__count">{draft.body.length} chars</span>
                <div style={{ display: 'flex', gap: 8 }}>
                  <button className="btn btn--ghost btn--sm" onClick={onClear}>Clear</button>
                  <button
                    className="btn btn--sm btn--wax"
                    disabled={!draft.body.trim()}
                    onClick={() => onSave("letters")}
                  >
                    <IcoEnvelope size={14} /> <span style={{marginLeft:6}}>Send & file</span>
                  </button>
                </div>
              </div>
            </>
          )}
        </div>
      </div>
    </div>
  );
}

/* =============================================================
 * SKETCHPAD
 * ============================================================= */
function SketchPad({ draft, setDraft, onSave }) {
  const ref = React.useRef(null);
  const drawingRef = React.useRef(false);  // ref instead of state — no re-render on every move event
  const last = React.useRef({ x: 0, y: 0 });

  // Set up canvas dimensions, scale, and stroke style.
  // Re-runs on window resize / orientation change so the sketch surface keeps
  // pace with the actual rendered size on mobile (rotation, on-screen keyboard).
  React.useEffect(() => {
    const setup = () => {
      const c = ref.current;
      if (!c) return;
      // Preserve any existing drawing across the resize so the user doesn't lose work.
      const prev = c.toDataURL ? c.toDataURL("image/png") : null;
      const dpr = window.devicePixelRatio || 1;
      c.width = c.offsetWidth * dpr;
      c.height = c.offsetHeight * dpr;
      const ctx = c.getContext("2d");
      ctx.scale(dpr, dpr);
      ctx.lineCap = "round";
      ctx.lineJoin = "round";
      ctx.strokeStyle = getComputedStyle(document.documentElement).getPropertyValue('--ink').trim() || '#1F2937';
      ctx.lineWidth = 1.8;
      if (prev) {
        const img = new Image();
        img.onload = () => ctx.drawImage(img, 0, 0, c.offsetWidth, c.offsetHeight);
        img.src = prev;
      }
    };
    setup();
    window.addEventListener('resize', setup);
    return () => window.removeEventListener('resize', setup);
  }, []);

  const getPoint = (e) => {
    const r = ref.current.getBoundingClientRect();
    return { x: e.clientX - r.left, y: e.clientY - r.top };
  };
  const start = (e) => {
    e.preventDefault();
    drawingRef.current = true;
    last.current = getPoint(e);
    ref.current.setPointerCapture?.(e.pointerId);
  };
  const move = (e) => {
    if (!drawingRef.current) return;
    e.preventDefault();
    const p = getPoint(e);
    const ctx = ref.current.getContext("2d");
    ctx.beginPath();
    ctx.moveTo(last.current.x, last.current.y);
    ctx.lineTo(p.x, p.y);
    ctx.stroke();
    last.current = p;
  };
  const end = () => { drawingRef.current = false; };
  const clear = () => {
    const c = ref.current;
    c.getContext("2d").clearRect(0, 0, c.width, c.height);
  };
  const save = () => {
    const c = ref.current;
    const url = c.toDataURL("image/png");
    onSave(url);
    clear();
  };

  return (
    <>
      <div className="sketchpad-wrap">
        <canvas
          ref={ref}
          className="sketchpad"
          onPointerDown={start}
          onPointerMove={move}
          onPointerUp={end}
          onPointerCancel={end}
          onPointerLeave={end}
        />
      </div>
      <div className="notebook__foot">
        <span className="notebook__count">blank page · ink only</span>
        <div style={{ display: 'flex', gap: 8 }}>
          <button className="btn btn--ghost btn--sm" onClick={clear}>Clear</button>
          <button className="btn btn--sm btn--clay" onClick={save}>
            <IcoFountainPen size={14} /> <span style={{marginLeft:6}}>Save sketch</span>
          </button>
        </div>
      </div>
    </>
  );
}

/* =============================================================
 * EXPORT — Take it with you
 *
 * Visitors own their notes. These helpers let them pull entries out as a
 * file they keep, in either a Lonesome Goat themed journal page (self-
 * contained HTML) or plain text. Power-user mode picks a folder on their
 * computer (Chrome/Edge File System API) so notes auto-save outside the
 * browser entirely.
 * ============================================================= */

// Format a YYYY-MM-DD-ish stamp suitable for filenames.
function _exportStamp() {
  const d = new Date();
  const yyyy = d.getFullYear();
  const mm = String(d.getMonth() + 1).padStart(2, '0');
  const dd = String(d.getDate()).padStart(2, '0');
  return `${yyyy}-${mm}-${dd}`;
}

function _slug(s) {
  return (s || "untitled")
    .toLowerCase()
    .replace(/[^a-z0-9\-\s]/g, '')
    .replace(/\s+/g, '-')
    .slice(0, 60) || "untitled";
}

function _escapeHTML(s) {
  return String(s || "")
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;');
}

// Render an entry's body to HTML paragraphs, leaving sketch markers as captions.
function _bodyToHTML(body) {
  if (!body) return "";
  return body.split(/\n{2,}/).map(p => {
    const cleaned = p.replace(/\{\{sketch:([a-z]+)\}\}/g, (_, k) => `<em class="lg-sketch-mark">[sketch — ${_escapeHTML(k)}]</em>`);
    return `<p>${cleaned.split('\n').map(_escapeHTML).join('<br/>')}</p>`;
  }).join('\n');
}

// Build a self-contained Lonesome Goat themed HTML journal page.
// Embeds the brand fonts via Google Fonts and a minimal slice of the site's
// visual system so the file looks like a real journal page, offline forever.
function buildJournalHTML(entries) {
  const list = Array.isArray(entries) ? entries : [entries];
  const articles = list.map(e => {
    const meta = [e.date, e.location, e.coords].filter(Boolean).map(_escapeHTML).join(' &middot; ');
    const tags = (e.tags || []).map(t => `<span class="tag">${_escapeHTML(t)}</span>`).join(' ');
    const sketchImg = e.png ? `<figure class="sketch"><img src="${_escapeHTML(e.png)}" alt=""/></figure>` : '';
    return `
    <article class="entry">
      <header>
        <span class="eyebrow">${_escapeHTML(e.category || (e.kind === 'sketches' ? 'Sketch' : e.kind === 'letters' ? 'Letter' : 'Field Note'))}</span>
        <h1 class="display">${_escapeHTML(e.title || 'Untitled')}</h1>
        ${meta ? `<div class="meta">${meta}</div>` : ''}
      </header>
      ${sketchImg}
      <section class="body serif">${_bodyToHTML(e.body)}</section>
      ${tags ? `<footer class="tags">${tags}</footer>` : ''}
    </article>`;
  }).join('\n');

  return `<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Lonesome Goat &mdash; Field Journal</title>
  <link rel="preconnect" href="https://fonts.googleapis.com" />
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
  <link href="https://fonts.googleapis.com/css2?family=Oswald:wght@400;500;600;700&family=Fraunces:opsz,wght@9..144,400;9..144,600;9..144,700&family=Cutive+Mono&family=Caveat:wght@400;500;600;700&display=swap" rel="stylesheet" />
  <style>
    :root {
      --paper: #F3EAD7; --paper-deep: #E0D3B0; --ink: #1F2937;
      --ink-soft: #2d3c4f; --ink-faint: #6f7a85; --gold: #C89A4E;
      --rule: rgba(31, 41, 55, 0.18);
    }
    * { box-sizing: border-box; }
    html, body { margin: 0; padding: 0; }
    body {
      background: var(--paper);
      color: var(--ink);
      font-family: "Fraunces", "Freight Text Pro", "Times New Roman", serif;
      line-height: 1.6;
      padding: 56px 24px 96px;
    }
    .page {
      max-width: 720px;
      margin: 0 auto;
    }
    .masthead {
      text-align: center;
      padding-bottom: 28px;
      margin-bottom: 48px;
      border-bottom: 2px solid var(--ink);
    }
    .masthead .lockup {
      font-family: "Oswald", "Rockwell", sans-serif;
      font-weight: 700;
      letter-spacing: 0.32em;
      text-transform: uppercase;
      font-size: 14px;
      color: var(--ink);
    }
    .masthead .tag {
      display: inline-block;
      margin-top: 6px;
      font-family: "Cutive Mono", monospace;
      font-size: 10px;
      letter-spacing: 0.28em;
      text-transform: uppercase;
      color: var(--ink-faint);
    }
    .entry { margin-bottom: 96px; }
    .entry + .entry { padding-top: 48px; border-top: 1px dashed var(--rule); }
    .entry header { margin-bottom: 28px; }
    .eyebrow {
      font-family: "Cutive Mono", monospace;
      font-size: 10px;
      letter-spacing: 0.32em;
      text-transform: uppercase;
      color: var(--gold);
      display: inline-block;
      margin-bottom: 12px;
    }
    h1.display {
      font-family: "Oswald", "Rockwell", sans-serif;
      font-weight: 600;
      font-size: clamp(32px, 6vw, 48px);
      line-height: 1.1;
      letter-spacing: -0.01em;
      color: var(--ink);
      margin: 0 0 12px;
    }
    .meta {
      font-family: "Cutive Mono", monospace;
      font-size: 11px;
      letter-spacing: 0.18em;
      text-transform: uppercase;
      color: var(--ink-faint);
    }
    .body p { font-size: 18px; margin: 0 0 18px; }
    .body em.lg-sketch-mark {
      display: block;
      font-family: "Caveat", cursive;
      font-style: normal;
      font-size: 16px;
      color: var(--ink-faint);
      text-align: center;
      margin: 12px 0;
    }
    figure.sketch { margin: 24px 0; text-align: center; }
    figure.sketch img { max-width: 100%; height: auto; }
    .tags { margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--rule); }
    .tags .tag {
      display: inline-block;
      margin: 0 6px 6px 0;
      padding: 3px 10px;
      background: var(--paper-deep);
      border: 1px solid var(--rule);
      font-family: "Cutive Mono", monospace;
      font-size: 10px;
      letter-spacing: 0.18em;
      text-transform: uppercase;
      color: var(--ink-soft);
    }
    .colophon {
      margin-top: 96px;
      padding-top: 32px;
      border-top: 1px solid var(--rule);
      text-align: center;
      font-family: "Caveat", cursive;
      color: var(--ink-faint);
    }
    .colophon .signoff {
      font-size: 26px;
      color: var(--ink);
    }
    .colophon .colophon__small {
      font-family: "Cutive Mono", monospace;
      font-size: 10px;
      letter-spacing: 0.28em;
      text-transform: uppercase;
      margin-top: 8px;
    }
    @media print {
      body { background: white; }
    }
  </style>
</head>
<body>
  <div class="page">
    <div class="masthead">
      <div class="lockup">Lonesome &middot; Goat</div>
      <div class="tag">Field Journal &middot; Filed ${_escapeHTML(_exportStamp())}</div>
    </div>
    ${articles}
    <div class="colophon">
      <div class="signoff">Stay Lonesome.</div>
      <div class="colophon__small">Yours, on your device, always</div>
    </div>
  </div>
</body>
</html>`;
}

// Plain text bundle. Readable in any editor, no styling.
function buildPlainText(entries) {
  const list = Array.isArray(entries) ? entries : [entries];
  const sep = "\n\n" + "—".repeat(40) + "\n\n";
  const parts = list.map(e => {
    const lines = [];
    lines.push((e.title || "Untitled").toUpperCase());
    if (e.date) lines.push(e.date);
    if (e.location) lines.push(e.location);
    if (e.coords) lines.push(e.coords);
    if (e.weather) lines.push("Wx: " + e.weather);
    lines.push("");
    if (e.body) lines.push(e.body);
    if (e.tags && e.tags.length) {
      lines.push("");
      lines.push("Tags: " + e.tags.join(", "));
    }
    return lines.join("\n");
  });
  const header = `LONESOME GOAT — FIELD JOURNAL\nFiled ${_exportStamp()}\n\n`;
  return header + parts.join(sep) + "\n";
}

// Trigger a browser download of arbitrary text content.
function downloadFile(content, filename, mime = "text/plain") {
  const blob = new Blob([content], { type: mime + ";charset=utf-8" });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  setTimeout(() => URL.revokeObjectURL(url), 1000);
}

async function copyToClipboard(text) {
  try {
    await navigator.clipboard.writeText(text);
    return true;
  } catch (_) {
    // Fallback for older browsers
    const ta = document.createElement('textarea');
    ta.value = text;
    document.body.appendChild(ta);
    ta.select();
    try { document.execCommand('copy'); document.body.removeChild(ta); return true; }
    catch (e) { document.body.removeChild(ta); return false; }
  }
}

function emailToSelf(text, subject) {
  const url = `mailto:?subject=${encodeURIComponent(subject || "Lonesome Goat — Field Journal")}&body=${encodeURIComponent(text)}`;
  window.location.href = url;
}

// File System Access API (Chrome/Edge/Brave). Lets the visitor pick a folder
// on their own computer where their notes auto-save as files. Maximum
// transparency: they can see the files in Finder/Explorer.
const _hasFileSystemAPI = () => typeof window !== 'undefined' && 'showDirectoryPicker' in window;

async function saveToFolder(filename, content) {
  if (!_hasFileSystemAPI()) {
    alert("Save-to-folder works in Chrome, Edge, or Brave. Use one of the regular Save options instead.");
    return false;
  }
  try {
    const dir = await window.showDirectoryPicker({ mode: 'readwrite' });
    const file = await dir.getFileHandle(filename, { create: true });
    const writable = await file.createWritable();
    await writable.write(content);
    await writable.close();
    return true;
  } catch (e) {
    if (e?.name !== 'AbortError') {
      console.error(e);
      alert("Couldn't save to that folder. Try one of the regular Save options.");
    }
    return false;
  }
}

/* ============================================================= */

function ExportMenu({ entries, label = "Export", title, scope = "single", onClose }) {
  const [open, setOpen] = React.useState(false);
  const [done, setDone] = React.useState(null); // "copied" | "emailed" | null

  const list = Array.isArray(entries) ? entries : [entries].filter(Boolean);
  const empty = list.length === 0;
  const single = list.length === 1;

  const baseName = single
    ? _slug(list[0]?.title) + "-" + _exportStamp()
    : "lonesome-goat-field-journal-" + _exportStamp();

  const close = () => { setOpen(false); setDone(null); onClose && onClose(); };

  const doHTML = () => { downloadFile(buildJournalHTML(list), baseName + ".html", "text/html"); close(); };
  const doTxt  = () => { downloadFile(buildPlainText(list), baseName + ".txt", "text/plain"); close(); };
  const doCopy = async () => { await copyToClipboard(buildPlainText(list)); setDone("copied"); setTimeout(close, 1100); };
  const doMail = () => { emailToSelf(buildPlainText(list), single ? (list[0].title || "Field Note") : "Lonesome Goat — Field Journal"); setDone("emailed"); setTimeout(close, 800); };
  const doFolder = async () => { const ok = await saveToFolder(baseName + ".html", buildJournalHTML(list)); if (ok) close(); };

  return (
    <div className="export-menu">
      <button
        className="btn btn--ghost btn--sm export-menu__trigger"
        onClick={() => setOpen(o => !o)}
        disabled={empty}
        title={empty ? "Nothing to export yet" : (title || "Take it with you")}>
        <IcoEnvelope size={14} /> <span style={{marginLeft:6}}>{label}</span>
      </button>
      {open && (
        <>
          <div className="export-menu__scrim" onClick={close} aria-hidden="true" />
          <div className="export-menu__sheet" role="menu">
            <div className="export-menu__head">
              <span className="eyebrow">Take it with you</span>
              <span className="export-menu__hint serif">
                {single
                  ? "Save this entry as a file you keep, anywhere you want."
                  : `${list.length} ${list.length === 1 ? 'entry' : 'entries'} \u00b7 yours alone, leave with all of them.`}
              </span>
            </div>
            <div className="export-menu__list">
              <button className="export-menu__item" onClick={doHTML}>
                <span className="export-menu__item-label">Save &middot; field journal page</span>
                <span className="export-menu__item-hint">HTML &middot; styled like Lonesome Goat &middot; works offline</span>
              </button>
              <button className="export-menu__item" onClick={doTxt}>
                <span className="export-menu__item-label">Save &middot; plain text</span>
                <span className="export-menu__item-hint">.txt &middot; opens in any editor</span>
              </button>
              <button className="export-menu__item" onClick={doCopy}>
                <span className="export-menu__item-label">{done === "copied" ? "Copied" : "Copy text to clipboard"}</span>
                <span className="export-menu__item-hint">paste anywhere</span>
              </button>
              <button className="export-menu__item" onClick={doMail}>
                <span className="export-menu__item-label">{done === "emailed" ? "Email opened\u2026" : "Email to me"}</span>
                <span className="export-menu__item-hint">opens your mail app, pre-filled</span>
              </button>
              {_hasFileSystemAPI() && (
                <button className="export-menu__item export-menu__item--power" onClick={doFolder}>
                  <span className="export-menu__item-label">Save to a folder I choose</span>
                  <span className="export-menu__item-hint">power option &middot; pick a folder on this device, your file lands there</span>
                </button>
              )}
            </div>
            <div className="export-menu__foot serif">
              Lonesome Goat doesn't keep your notes. They live on your device. Always yours.
            </div>
          </div>
        </>
      )}
    </div>
  );
}

/* =============================================================
 * PRIVACY NOTICE — visible always, expandable explanation.
 *
 * The Field Desk runs entirely on the visitor's device (browser localStorage).
 * No server, no accounts, no shared backend. This component makes that
 * promise plain in Lonesome Goat voice so visitors trust the surface.
 * ============================================================= */
function PrivacyNotice() {
  const [open, setOpen] = React.useState(false);
  return (
    <div className={"desk-privacy" + (open ? " desk-privacy--open" : "")}>
      <div className="desk-privacy__row">
        <span className="desk-privacy__stamp">FILED &middot; YOUR DEVICE</span>
        <span className="desk-privacy__line serif">Always yours.</span>
        <button
          type="button"
          className="desk-privacy__how"
          onClick={() => setOpen(o => !o)}
          aria-expanded={open}>
          {open ? "Close" : "How this works"}
        </button>
      </div>
      {open && (
        <div className="desk-privacy__expand serif">
          <p>
            Lonesome Goat doesn't keep a server for your notes. Anything you write
            here lives in this browser, on your own device. We can't read it. Nothing
            is uploaded or shared.
          </p>
          <p>
            To take your notes with you, hit <strong>Take it with you</strong> at the
            top &mdash; save them as a Lonesome Goat journal page, plain text, copy
            them, or email them to yourself. Save them anywhere you want.
          </p>
          <p>
            Clear your browser, switch computers, or use a private tab and the notes
            on this device are gone. That's the trade-off, and the point. Always
            your call.
          </p>
        </div>
      )}
    </div>
  );
}

/* =============================================================
 * FIELD DESK — root component
 * ============================================================= */
function FieldDesk({ go }) {
  useLGStore();
  const [openFolder, setOpenFolder] = React.useState(null);
  const [mode, setMode] = React.useState("write");
  const [readingNote, setReadingNote] = React.useState(null);
  const [lampOn, setLampOn] = React.useState(true);
  const [draft, setDraft] = React.useState({
    title: "", body: "", to: "", location: "", coords: "", tags: [], editId: null, editKind: null
  });
  // Welcome letter — shows on first visit, dismissible (persisted in localStorage).
  const [showLetter, setShowLetter] = React.useState(() => {
    try { return localStorage.getItem("lg.deskLetter.dismissed") !== "1"; }
    catch (_) { return true; }
  });
  const dismissLetter = () => {
    setShowLetter(false);
    try { localStorage.setItem("lg.deskLetter.dismissed", "1"); } catch (_) { /* ignore */ }
  };

  React.useEffect(() => {
    const cur = LGStore.activeDraft();
    if (cur) setDraft({ tags: [], editId: null, editKind: null, ...cur });
  }, []);
  React.useEffect(() => {
    const id = setTimeout(() => LGStore.setActiveDraft(draft), 400);
    return () => clearTimeout(id);
  }, [draft]);

  const counts = {
    entries:  LGStore.list("entries").length + NOTES.length,
    drafts:   LGStore.list("drafts").length,
    sketches: LGStore.list("sketches").length,
    letters:  LGStore.list("letters").length,
  };

  // Everything THIS visitor has written (not the seeded NOTES) — used by
  // the "Take it with you" bulk Export. Their notes alone, never ours.
  const allMine = React.useMemo(() => [
    ...LGStore.list("entries").map(x => ({ ...x, kind: "entries" })),
    ...LGStore.list("drafts").map(x => ({ ...x, kind: "drafts" })),
    ...LGStore.list("sketches").map(x => ({ ...x, kind: "sketches" })),
    ...LGStore.list("letters").map(x => ({ ...x, kind: "letters" })),
  ], [counts.entries, counts.drafts, counts.sketches, counts.letters]);

  // Combined readable items for the read-mode shelf
  const readableItems = React.useMemo(() => {
    const seeded = NOTES.map(n => ({
      ...n,
      kind: "entries",
      seed: true,
      _ts: 0
    }));
    const live = []
      .concat(LGStore.list("entries").map(x => ({ ...x, kind: "entries" })))
      .concat(LGStore.list("sketches").map(x => ({ ...x, kind: "sketches" })))
      .concat(LGStore.list("letters").map(x => ({ ...x, kind: "letters" })));
    // sort: live by _ts desc, then seeded by their order
    live.sort((a, b) => (b._ts || 0) - (a._ts || 0));
    return [...live, ...seeded];
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [counts.entries, counts.sketches, counts.letters]);

  const blankDraft = () => ({
    title: "", body: "", to: "", location: "", coords: "", tags: [], editId: null, editKind: null
  });

  const save = (kind) => {
    if (!draft.body.trim()) return;
    const wordCount = draft.body.split(/\s+/).filter(Boolean).length;
    const item = {
      kind,
      title: draft.title || (kind === "letters" ? `To ${draft.to || "—"}` : "Untitled"),
      to: draft.to,
      date: todayStamp(),
      location: (draft.location || "").trim() || "n/a",
      coords:   (draft.coords   || "").trim() || "n/a",
      weather:  (draft.weather  || "").trim() || "n/a",
      wordCount,
      tags: draft.tags || [],
      heroImage: draft.heroImage || "",
      body: draft.body,
      preview: draft.body.slice(0, 80).replace(/\s+/g, " ") + (draft.body.length > 80 ? "…" : "")
    };
    if (draft.editId && draft.editKind === kind) {
      // edit-in-place — keep id & ts
      LGStore.update(kind, draft.editId, item);
    } else {
      // if user is editing something but switching kinds, remove the old item
      if (draft.editId && draft.editKind && draft.editKind !== kind) {
        LGStore.remove(draft.editKind, draft.editId);
      }
      LGStore.add(kind, item);
    }
    LGStore.clearActiveDraft();
    setDraft(blankDraft());
    flashStamp(kind);
  };

  const saveSketch = (pngUrl) => {
    const item = {
      kind: "sketches",
      title: draft.title || "Untitled sketch",
      date: todayStamp(),
      tags: draft.tags || [],
      preview: "field sketch",
      png: pngUrl
    };
    if (draft.editId && draft.editKind === "sketches") {
      LGStore.update("sketches", draft.editId, item);
    } else {
      LGStore.add("sketches", item);
    }
    setDraft(blankDraft());
    flashStamp("sketches");
  };

  const flashStamp = (kind) => {
    const el = document.getElementById("filed-stamp");
    if (!el) return;
    el.dataset.kind = kind;
    el.classList.add("filed-stamp--show");
    setTimeout(() => el.classList.remove("filed-stamp--show"), 1800);
  };

  const clearDraft = () => {
    setDraft(blankDraft());
    LGStore.clearActiveDraft();
  };

  const pinItem = (item) => {
    LGStore.setPinned(item);
    setOpenFolder(null);
  };
  const pinned = LGStore.pinned();

  /* Open into reader (default action). */
  const pickItem = (item) => {
    if (item.kind === "entries" || item.seed) {
      setReadingNote(item);
      setMode("read");
    } else if (item.kind === "drafts") {
      // drafts open straight into edit
      editItem(item);
      return;
    } else if (item.kind === "letters") {
      setReadingNote({ ...item, location: item.to ? "To: " + item.to : "" });
      setMode("read");
    } else if (item.kind === "sketches") {
      setReadingNote(item);
      setMode("read");
    }
    setOpenFolder(null);
  };

  /* Edit-after-publish: load item back into draft + matching mode. */
  const editItem = (item) => {
    if (item.seed) return; // seeded NOTES are read-only
    const kind = item.kind;
    const newMode = kind === "sketches" ? "sketch" : kind === "letters" ? "letter" : "write";
    setDraft({
      title: item.title || "",
      body: item.body || "",
      to: item.to || "",
      location: item.location || "",
      coords: item.coords || "",
      tags: item.tags || [],
      editId: item.id,
      editKind: kind
    });
    setMode(newMode);
    setReadingNote(null);
    setOpenFolder(null);
  };

  const deleteItem = (kind, id) => {
    LGStore.remove(kind, id);
  };

  const usePrompt = (text) => {
    setMode("write");
    setReadingNote(null);
    setDraft(d => ({
      ...d,
      body: d.body ? d.body : text + "\n\n",
      // soft-suggest a title only when blank
      title: d.title
    }));
  };

  return (
    <main className={"desk-room" + (lampOn ? "" : " desk-room--dim")}>
      <div className="desk-room__inner">
        <header className="desk-head">
          <div>
            <span className="eyebrow">The Field Desk</span>
            <h1 className="display desk-head__title">Sit down. Stay a while.</h1>
          </div>
          <div className="desk-head__tools">
            <ExportMenu
              entries={allMine}
              label="Take it with you"
              title="Export everything you've written"
              scope="bulk"
            />
            <button className={"toolbtn" + (lampOn ? " toolbtn--on" : "")} onClick={() => setLampOn(v => !v)} title="Toggle lamp">
              <IcoLamp size={16} /> Lamp
            </button>
            <span className="toolbtn toolbtn--static" title="Today">{todayStamp()}</span>
          </div>
        </header>

        {/* Privacy promise — always visible. Lives between the head and the
            welcome letter so visitors see it on every visit, not just the first.
            Voice: plainspoken, "always yours" anchor language. */}
        <PrivacyNotice />


        {/* Welcome letter — first-visit explanation of the Field Desk in Lonesome Goat's voice. */}
        {showLetter && (
          <aside className="desk-letter">
            <button className="desk-letter__close" onClick={dismissLetter} aria-label="Dismiss letter" title="I've read this">
              <IcoX size={14} />
            </button>
            <div className="desk-letter__head">
              <span className="eyebrow">A letter, for the new arrival</span>
            </div>
            <div className="desk-letter__body">
              <p>Friend,</p>
              <p>
                Welcome to the Field Desk. This is where the work happens —
                quietly, on paper, on your own time.
              </p>
              <p>
                Use <strong>Write</strong> to file a Goat Note when something is worth remembering — a road, a fire, a quiet morning, a story earned the hard way.{" "}
                Use <strong>Read</strong> to flip back through what you've filed.{" "}
                Use <strong>Sketch</strong> to draw a small pen-and-paper plate the way the old field journals did.{" "}
                Use <strong>Letter</strong> to write to someone, not to the world.
              </p>
              <p>Take your time. Lonesome Goat is filed slowly, on purpose.</p>
            </div>
            <div className="desk-letter__sign">
              <span className="desk-letter__hand">Stay Lonesome,</span>
              <span className="desk-letter__sig">Lonesome Goat</span>
            </div>
          </aside>
        )}

        <div className="desk-top">
          <PinnedSlot
            pinned={pinned}
            onClear={() => LGStore.setPinned(null)}
            onPick={() => setOpenFolder("entries")}
          />
          <DailyPromptCard onUse={usePrompt} />
        </div>

        <FolderTabs open={openFolder} setOpen={setOpenFolder} counts={counts} />

        <div className="desk-surface desk-surface--single">
          {lampOn && <div className="desk-surface__pool" aria-hidden="true" />}

          <div className="desk-surface__notebook">
            {draft.editId && (
              <div className="edit-banner">
                <IcoPencil size={14} />
                <span>Editing — changes save in place</span>
                <button className="edit-banner__cancel" onClick={clearDraft}>Discard changes</button>
              </div>
            )}
            {/* Render reading mode for sketches inline */}
            {mode === "read" && readingNote?.kind === "sketches" ? (
              <div className="notebook">
                <div className="notebook__page">
                  <Letterhead mode={mode} setMode={setMode} draft={draft} setDraft={setDraft} note={readingNote} />
                  <div className="notebook__body">
                    <button
                      className="reader-back"
                      onClick={() => setReadingNote(null)}
                      title="Back to the shelf"
                    >
                      <IcoChevron size={14} dir="left" />
                      <span>Back to the shelf</span>
                    </button>
                    <div className="sketch-view">
                      {readingNote.png && <img src={readingNote.png} alt={readingNote.title || "Sketch"} />}
                    </div>
                    <div className="reader-actions">
                      {!readingNote.seed && (
                        <button className="btn btn--ghost btn--sm" onClick={() => editItem(readingNote)}>
                          <IcoPencil size={14} /> <span style={{marginLeft:6}}>Edit</span>
                        </button>
                      )}
                      <ExportMenu entries={readingNote} label="Take it with you" title="Save this sketch as a file you keep" />
                    </div>
                  </div>
                </div>
              </div>
            ) : (
              <Notebook
                mode={mode}
                setMode={setMode}
                draft={draft}
                setDraft={setDraft}
                readingNote={readingNote}
                onSave={save}
                onSaveSketch={saveSketch}
                onClear={clearDraft}
                onEditNote={editItem}
                readableItems={readableItems}
                onPickItem={pickItem}
                setReadingNote={setReadingNote}
              />
            )}
            <div id="filed-stamp" className="filed-stamp">
              <span className="stamp stamp--ink filed-stamp__mark">FILED · {todayStamp()}</span>
            </div>
          </div>

          {openFolder && (
            <FolderTray
              id={openFolder}
              onClose={() => setOpenFolder(null)}
              onPick={pickItem}
              onPin={pinItem}
              onEdit={editItem}
              onDelete={deleteItem}
            />
          )}
        </div>

        <footer className="desk-foot">
          <span className="hand" style={{ fontSize: 22, color: 'var(--ink-soft)' }}>
            "We file them slowly. We mean every word."
          </span>
          <SignOff size={28} />
        </footer>
      </div>
    </main>
  );
}

window.FieldDesk = FieldDesk;
// Expose so the Ritual modal (and any other entry flow) can reuse the
// exact same picker UIs as the Field Desk.
window.StampPicker = StampPicker;
window.TagPicker = TagPicker;
