// Home view, header, family portrait, cards. Depends on icons.jsx.

const { useState: useStateHome, useEffect: useEffectHome, useMemo, useRef } = React;

const MEAL_TYPES = ["Breakfast","Brunch","Lunch","Dinner","Sides","Dessert","Snack","Appetizer","Drink","Marinades"];
const MEAT_TYPES = ["Beef","Chicken","Pork","Fish","Seafood","Lamb","Turkey","Vegetarian","Vegan"];

// ── Quick & Easy helpers ───────────────────────────────────────────
function parseMinutes(str) {
  if (!str) return null;
  const s = String(str).toLowerCase().trim();
  // "1 hr 30 min", "1 hour 30 minutes", "2 hrs"
  const hrMin = s.match(/(\d+)\s*h(?:r|rs|our|ours)?\s*(?:(\d+)\s*m(?:in|ins|inutes?)?)?/);
  if (hrMin) return parseInt(hrMin[1]) * 60 + (hrMin[2] ? parseInt(hrMin[2]) : 0);
  // "45 min", "45 minutes", "45 mins"
  const minOnly = s.match(/(\d+)\s*m(?:in|ins|inutes?)/);
  if (minOnly) return parseInt(minOnly[1]);
  return null;
}

function isQuickAndEasy(recipe) {
  if (recipe.quickAndEasy) return true;
  const prep = parseMinutes(recipe.prep) ?? 0;
  const cook = parseMinutes(recipe.cook) ?? 0;
  return prep + cook > 0 && prep + cook <= 20;
}

// ── Tiny UI primitives ──────────────────────────────────────────────
function Stars({ value = 0, size = 14 }) {
  return (
    <span className="stars-row" aria-label={`${value} out of 5`}>
      {[1, 2, 3, 4, 5].map(n => (
        <IconStar key={n} size={size} stroke={1.6} filled={n <= value} />
      ))}
    </span>
  );
}

function NameFilter({ label, icon: Ic, values, value, onChange }) {
  const [open, setOpen] = useStateHome(false);
  const rootRef = useRef(null);

  useEffectHome(() => {
    if (!open) return;
    const onDoc = (e) => { if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false); };
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    document.addEventListener('keydown', onKey);
    return () => {
      document.removeEventListener('mousedown', onDoc);
      document.removeEventListener('keydown', onKey);
    };
  }, [open]);

  if (!values || values.length === 0) return null;

  return (
    <div className="author-filter" ref={rootRef}>
      <button
        type="button"
        className={`pill ${value ? 'is-active' : ''}`}
        onClick={() => setOpen(o => !o)}
        aria-haspopup="listbox"
        aria-expanded={open}
      >
        {Ic && <Ic size={13} stroke={1.7} />}
        {value ? `${label}: ${value}` : label}
        <IconChevDown size={12} style={{ marginLeft: 2 }} />
      </button>
      {open && (
        <div className="author-pop" role="listbox">
          <div className="author-pop-head">Filter by {label.toLowerCase()}</div>
          <div className="author-pop-list">
            <button
              type="button"
              className={`author-pop-item ${!value ? 'on' : ''}`}
              onClick={() => { onChange(''); setOpen(false); }}
              role="option" aria-selected={!value}
            >
              Anyone
            </button>
            {values.map(v => (
              <button
                key={v}
                type="button"
                className={`author-pop-item ${value === v ? 'on' : ''}`}
                onClick={() => { onChange(v); setOpen(false); }}
                role="option" aria-selected={value === v}
              >
                {v}
              </button>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

function Pill({ children, tone = "default", onClick, active }) {
  return (
    <button
      className={`pill pill-${tone} ${active ? "is-active" : ""}`}
      onClick={onClick}
      type="button"
    >
      {children}
    </button>
  );
}

function CountUp({ value }) {
  const [n, setN] = useStateHome(0);
  useEffectHome(() => {
    if (matchMedia('(prefers-reduced-motion: reduce)').matches) { setN(value); return; }
    let raf;
    const t0 = performance.now();
    const tick = (t) => {
      const p = Math.min(1, (t - t0) / 1200);
      setN(Math.round(value * (1 - Math.pow(1 - p, 3))));
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [value]);
  return <>{n}</>;
}

function MetaStat({ icon: Ic, label, value }) {
  return (
    <div className="meta-stat">
      <Ic size={18} stroke={1.5} />
      <div>
        <div className="meta-stat-label">{label}</div>
        <div className="meta-stat-value">{value}</div>
      </div>
    </div>
  );
}

// ── Header ──────────────────────────────────────────────────────────
// v2 chrome: family-name wordmark, text nav with underline actives,
// "+ New recipe" menu (write / import / scan), mobile bottom tab bar.
function Header({ onHome, onSearch, searchValue, onAdd, onImport, onScan, onShopping, shoppingCount, onRoulette, onPlanner, onFamily, isSignedIn, currentView, familyName }) {
  const [newOpen, setNewOpen] = useStateHome(false);

  useEffectHome(() => {
    if (!newOpen) return;
    const close = (e) => { if (!e.target.closest('.new-menu-wrap')) setNewOpen(false); };
    document.addEventListener('click', close);
    return () => document.removeEventListener('click', close);
  }, [newOpen]);

  const navTo = (fn) => () => { setNewOpen(false); fn(); };
  const newItem = (fn) => () => { setNewOpen(false); fn(); };

  const navLinks = [
    { key: 'home',     label: 'Cookbook', go: onHome },
    { key: 'planner',  label: 'Planner',  go: onPlanner },
    { key: 'roulette', label: 'Roulette', go: onRoulette },
    { key: 'family',   label: 'Family',   go: onFamily, authOnly: true },
  ];

  return (
    <>
      <header className="app-header">
        <button className="logo-btn" onClick={onHome}>
          <span className="wm-name">{familyName || 'The Family Cookbook'}</span>
          <span className="wm-sub">the family collection</span>
        </button>

        <div className="search-bar">
          <IconSearch size={18} stroke={1.7} />
          <input
            type="text"
            placeholder="Search recipes, ingredients, authors…"
            value={searchValue}
            onChange={e => onSearch(e.target.value)}
          />
          {searchValue && (
            <button className="search-clear" onClick={() => onSearch("")}>
              <IconX size={14} />
            </button>
          )}
        </div>

        <nav className="header-nav">
          {navLinks.map(l => (l.authOnly && !isSignedIn) ? null : (
            <button key={l.key}
              className={`nav-link${currentView === l.key ? ' active' : ''}`}
              onClick={navTo(l.go)}>
              {l.label}
            </button>
          ))}
        </nav>

        <div className="header-actions">
          <button className="ghost-btn list-btn" onClick={onShopping} title="Shopping list">
            <IconBag size={18} />
            <span className="list-label">List</span>
            {shoppingCount > 0 && <span className="badge">{shoppingCount}</span>}
          </button>
          {isSignedIn && (
            <div className="new-menu-wrap">
              <button className="primary-btn" onClick={() => setNewOpen(o => !o)}>
                <IconPlus size={18} stroke={2} />
                <span className="new-label">New recipe</span>
              </button>
              {newOpen && (
                <div className="new-menu">
                  <p className="new-menu-head">add to the box</p>
                  <button onClick={newItem(onImport)}>
                    <IconLink size={16} />
                    <span><b>Import from web</b><small>paste a link, we pull the recipe</small></span>
                  </button>
                  <button onClick={newItem(onScan)}>
                    <IconScan size={16} />
                    <span><b>Scan a card</b><small>photograph it, we transcribe</small></span>
                  </button>
                  <button onClick={newItem(onAdd)}>
                    <IconPlus size={16} />
                    <span><b>Write it down</b><small>type it in yourself</small></span>
                  </button>
                </div>
              )}
            </div>
          )}
          <AuthButton />
        </div>
      </header>

      <nav className="tabbar" aria-label="Primary">
        {navLinks.map(l => (l.authOnly && !isSignedIn) ? null : (
          <button key={l.key}
            className={`tab${currentView === l.key ? ' active' : ''}`}
            onClick={navTo(l.go)}>
            {l.key === 'home'     && <IconBook size={22} />}
            {l.key === 'planner'  && <IconCalendar size={22} />}
            {l.key === 'roulette' && <IconDice size={22} />}
            {l.key === 'family'   && <IconUsers size={22} />}
            <span>{l.label}</span>
          </button>
        ))}
      </nav>
    </>
  );
}

// ── Family portrait module ─────────────────────────────────────────
function FamilyPortrait({ photo, onPhotoChange, familyName, onNameChange, tagline, recipes, onOpen, onRoulette, onPlanner }) {
  const fileRef = useRef(null);
  const [editing, setEditing]     = useStateHome(false);
  const [uploading, setUploading] = useStateHome(false);
  const [uploadErr, setUploadErr] = useStateHome('');

  const pickFile = () => fileRef.current?.click();
  const onFile = async (e) => {
    const f = e.target.files?.[0];
    if (!f) return;
    if (fileRef.current) fileRef.current.value = '';
    setUploading(true);
    setUploadErr('');
    try {
      await onPhotoChange(f);
    } catch (err) {
      setUploadErr(err.message || 'Upload failed');
    } finally {
      setUploading(false);
    }
  };

  const dinnerPool = recipes.filter(r => !(r.mealTypes || []).includes('Marinades'));
  const tonightPick = recipes.find(r => (r.hearts || []).length > 0)
    || dinnerPool[Math.floor(Math.random() * dinnerPool.length)]
    || recipes[0];
  const year = new Date().getFullYear();
  const recipeCount = recipes.length;

  return (
    <section className="family">
      <div className="family-photo-wrap">
        <div className="family-photo-frame">
          <span className="photo-tape" aria-hidden="true" />
          {uploading ? (
            <div className="family-photo-uploading">
              <div className="photo-upload-spinner" />
              <div className="photo-upload-label">Uploading…</div>
            </div>
          ) : photo ? (
            <img src={photo} alt={familyName} className="family-photo" />
          ) : (
            <div className="family-photo-empty" onClick={pickFile}>
              <div className="empty-inner">
                <IconCamera size={32} stroke={1.3} />
                <div className="empty-title">Add your family photo</div>
                <div className="empty-hint">A grandparent in the kitchen. A Sunday dinner.<br/>Something that means home.</div>
                <button className="empty-btn">
                  <IconPlus size={14} stroke={2} /> Choose a photo
                </button>
              </div>
            </div>
          )}
          {photo && !uploading && (
            <button className="family-photo-edit" onClick={pickFile} title="Replace photo">
              <IconCamera size={14} /> Replace
            </button>
          )}
          {uploadErr && <div className="family-photo-err">{uploadErr}</div>}
          <input ref={fileRef} type="file" accept="image/*" hidden onChange={onFile} />
        </div>
        <div className="family-caption">
          <span className="caption-mark">"</span>
          Every recipe in here is someone's memory."
        </div>
      </div>

      <div className="family-content">
        <div className="family-eyebrow">
          <span className="eyebrow-line" />
          Volume {Math.max(1, year - 2020)}
        </div>
        {editing ? (
          <input
            className="family-name-input"
            value={familyName}
            onChange={(e) => onNameChange(e.target.value)}
            onBlur={() => setEditing(false)}
            onKeyDown={(e) => e.key === "Enter" && setEditing(false)}
            autoFocus
          />
        ) : (
          <h1 className="family-name" onClick={() => setEditing(true)} title="Click to edit">
            {familyName}
            <span className="family-name-edit"><IconEdit size={14} /></span>
          </h1>
        )}
        <p className="family-tagline">{tagline}</p>

        <div className="family-stats">
          {[
            { n: recipeCount, label: recipeCount === 1 ? 'recipe' : 'recipes' },
            { n: new Set(recipes.map(r => (r.addedBy || '').trim()).filter(Boolean)).size, label: 'contributors' },
            { n: recipes.filter(r => (r.hearts || []).length > 0).length, label: 'favorites' },
          ].filter(st => st.n > 0).map(st => (
            <div className="fam-stat" key={st.label}>
              <div className="fam-stat-num"><CountUp value={st.n} /></div>
              <div className="fam-stat-label">{st.label}</div>
            </div>
          ))}
        </div>
        {(() => {
          const last = recipes.reduce((a, r) => (!a || r.id > a.id) ? r : a, null);
          const who = last && (last.addedBy || '').split(' ')[0];
          return who ? <p className="fam-last-added">last added by {who}</p> : null;
        })()}

        {tonightPick && (
          <button className="tonight-pick" onClick={() => onOpen(tonightPick.id)}>
            {tonightPick.image && (
              <div className="tp-thumb" style={{ backgroundImage: `url(${tonightPick.image})` }} />
            )}
            <div className="tp-body">
              <div className="tp-eyebrow">Tonight's pick</div>
              <div className="tp-title">{tonightPick.name}</div>
              <div className="tp-meta">
                {(tonightPick.prep || tonightPick.cook) && (
                  <><IconClock size={12} /> {tonightPick.prep && tonightPick.cook ? `${tonightPick.prep} + ${tonightPick.cook}` : (tonightPick.prep || tonightPick.cook)}</>
                )}
                {(tonightPick.prep || tonightPick.cook) && tonightPick.servings ? ' · ' : ''}
                {tonightPick.servings && (<><IconUsers size={12} /> {tonightPick.servings}</>)}
              </div>
            </div>
            <IconArrowRight size={18} className="tp-arrow" />
          </button>
        )}

        <div className="hero-acts">
          {onRoulette && (
            <button className="hero-act" onClick={onRoulette}>
              <IconDice size={18} />
              <span>Spin for dinner</span>
            </button>
          )}
          {onPlanner && (
            <button className="hero-act" onClick={onPlanner}>
              <IconCalendar size={18} />
              <span>This week's plan</span>
            </button>
          )}
        </div>
      </div>
    </section>
  );
}

// Average star rating from the per-person ratings array
function avgRating(r) {
  const list = Array.isArray(r.ratings) ? r.ratings : [];
  if (!list.length) return 0;
  const sum = list.reduce((a, x) => a + (Number(x.stars) || 0), 0);
  return sum / list.length;
}

// ── Home / gallery ──────────────────────────────────────────────────
const COLLECTION_PAGE = 12;

function HomeView({
  recipes, onOpen, filters, setFilters, cardStyle,
  familyPhoto, setFamilyPhoto, familyName, setFamilyName, familyTagline, onRoulette, onPlanner,
  onToggleFav, members, myUserId,
}) {
  const myHeart = (r) => (r.hearts || []).includes(myUserId);
  const { search, meal, meat, addedBy, author, favOnly, quickEasy } = filters;
  const [viewMode, setViewMode] = useStateHome(() => localStorage.getItem('cb.viewMode') || 'cards');
  const [limit, setLimit] = useStateHome(COLLECTION_PAGE);
  const filterSig = [search, meal, meat, addedBy, author, favOnly, quickEasy].join('|');
  useEffectHome(() => { setLimit(COLLECTION_PAGE); }, [filterSig]);
  const pickView = (m) => { setViewMode(m); localStorage.setItem('cb.viewMode', m); };

  const filtered = useMemo(() => {
    const q = search.trim().toLowerCase();
    return recipes.filter(r => {
      if (favOnly    && !myHeart(r))                             return false;
      if (quickEasy  && !isQuickAndEasy(r))                      return false;
      if (meal       && !(r.mealTypes || []).includes(meal))     return false;
      if (meat       && !(r.meatTypes || []).includes(meat))     return false;
      if (addedBy    && (r.addedBy || '') !== addedBy)           return false;
      if (author     && (r.author  || '') !== author)            return false;
      if (q) {
        const hay = [r.name, r.description, r.author, r.addedBy, r.category,
                     ...(r.ingredients || []).map(i => i.name)].join(" ").toLowerCase();
        if (!hay.includes(q)) return false;
      }
      return true;
    });
  }, [recipes, search, meal, meat, addedBy, author, favOnly, quickEasy]);

  const addedByList = useMemo(() =>
    [...new Set(recipes.map(r => (r.addedBy || '').trim()).filter(Boolean))].sort((a, b) => a.localeCompare(b)),
    [recipes]);
  const authorList = useMemo(() =>
    [...new Set(recipes.map(r => (r.author || '').trim()).filter(Boolean))].sort((a, b) => a.localeCompare(b)),
    [recipes]);

  const favorites = recipes.filter(myHeart).slice(0, 4);
  const hasActiveFilter = meal || meat || addedBy || author || favOnly || quickEasy || search.trim();

  return (
    <div className="home">
      {!hasActiveFilter && (
        <FamilyPortrait
          photo={familyPhoto}
          onPhotoChange={setFamilyPhoto}
          familyName={familyName}
          onNameChange={setFamilyName}
          tagline={familyTagline}
          recipes={recipes}
          onOpen={onOpen}
          onRoulette={onRoulette}
          onPlanner={onPlanner}
        />
      )}

      {!hasActiveFilter && favorites.length > 0 && (
        <section className="rail">
          <div className="section-head">
            <div>
              <div className="section-eyebrow">Saved</div>
              <h2 className="section-title">Your favorites</h2>
            </div>
          </div>
          <div className="rail-cards">
            {favorites.map(r => (
              <MiniCard key={r.id} recipe={r} onClick={() => onOpen(r.id)} />
            ))}
          </div>
        </section>
      )}

      <section className="filters">
        <div className="filter-group">
          <NameFilter
            label="Added by"
            icon={IconUsers}
            values={addedByList}
            value={addedBy}
            onChange={(v) => setFilters(f => ({ ...f, addedBy: v }))}
          />
          <NameFilter
            label="Author"
            icon={IconBook}
            values={authorList}
            value={author}
            onChange={(v) => setFilters(f => ({ ...f, author: v }))}
          />
          <span className="filter-divider" />
          <Pill active={favOnly} onClick={() => setFilters(f => ({ ...f, favOnly: !f.favOnly }))}>
            <IconHeart size={14} filled={favOnly} /> Favorites
          </Pill>
          <Pill active={quickEasy} onClick={() => setFilters(f => ({ ...f, quickEasy: !f.quickEasy }))}>
            ⚡ Quick &amp; Easy
          </Pill>
          <span className="filter-divider" />
          {MEAL_TYPES.map(m => (
            <Pill key={m} active={meal === m}
              onClick={() => setFilters(f => ({ ...f, meal: f.meal === m ? "" : m }))}>
              {m}
            </Pill>
          ))}
        </div>
        <div className="filter-group">
          {MEAT_TYPES.map(m => (
            <Pill key={m} tone="outline" active={meat === m}
              onClick={() => setFilters(f => ({ ...f, meat: f.meat === m ? "" : m }))}>
              {m}
            </Pill>
          ))}
        </div>
      </section>

      <section className="results">
        <div className="section-head">
          <div>
            <div className="section-eyebrow">
              {hasActiveFilter ? `${filtered.length} ${filtered.length === 1 ? "result" : "results"}` : "The collection"}
            </div>
            <h2 className="section-title">
              {hasActiveFilter ? "Matching recipes" : "All recipes"}
            </h2>
          </div>
          <div className="view-toggle">
            <button className={viewMode === 'cards' ? 'on' : ''} onClick={() => pickView('cards')}>Cards</button>
            <button className={viewMode === 'index' ? 'on' : ''} onClick={() => pickView('index')}>Index</button>
          </div>
        </div>

        {filtered.length === 0 ? (
          <div className="empty">
            <IconSearch size={32} stroke={1.2} />
            <p>No recipes match. Try clearing a filter.</p>
          </div>
        ) : viewMode === 'index' ? (
          <div className="index-list">
            {filtered.map(r => (
              <button key={r.id} className="irow" onClick={() => onOpen(r.id)}>
                <span className="i-no">No. {String(r.id).padStart(3, '0')}</span>
                <span className="i-t">{r.name}</span>
                <span className="i-by">{r.addedBy ? `${r.addedBy}'s` : (r.author || '')}</span>
                <span className="i-time">{r.prep || r.cook || ''}</span>
                <span className="i-loved">{(r.hearts || []).length > 0 ? `♥ ${(r.hearts || []).length}` : ''}</span>
              </button>
            ))}
          </div>
        ) : (
          <>
            <div className={`grid grid-${cardStyle}`} key={filterSig}>
              {filtered.slice(0, limit).map((r, i) => (
                <RecipeCard key={r.id} recipe={r} dealIndex={i} onClick={() => onOpen(r.id)} style={cardStyle} onToggleFav={onToggleFav} members={members} myUserId={myUserId} />
              ))}
            </div>
            {filtered.length > limit && (
              <button className="ghost-btn show-more" onClick={() => setLimit(l => l + COLLECTION_PAGE)}>
                Show {Math.min(COLLECTION_PAGE, filtered.length - limit)} more · {filtered.length - limit} still in the box
              </button>
            )}
          </>
        )}
      </section>
    </div>
  );
}

function RecipeCard({ recipe, onClick, style, onToggleFav, members = [], myUserId = null, dealIndex = 0 }) {
  const mark = memberAvatarFor(members, recipe.addedBy || recipe.author);
  const hearts = recipe.hearts || [];
  const mine = hearts.includes(myUserId);
  const [flipped, setFlipped] = useStateHome(false);
  const img = recipe.image || '';
  const lined = !img;
  const no = String(recipe.id).padStart(3, '0');
  const time = recipe.prep && recipe.cook ? `${recipe.prep} + ${recipe.cook}` : recipe.prep || recipe.cook || '';
  const notes = [];
  if (recipe.description) notes.push({ who: recipe.addedBy || recipe.author || 'the cook', text: recipe.description });
  (recipe.comments || []).forEach(c => notes.push({ who: c.author, text: c.text }));
  const hasBack = notes.length > 0;

  const flip = (e) => { e.stopPropagation(); setFlipped(f => !f); };
  const heart = (e) => { e.stopPropagation(); onToggleFav && onToggleFav(recipe); };

  return (
    <article
      className={`vcard${flipped ? ' flipped' : ''}${lined ? ' lined' : ''}`}
      style={{ animationDelay: `${Math.min(dealIndex, 11) * 0.05}s` }}
      onClick={() => { if (!flipped) onClick(); }}
    >
      <div className="cin">
        <div className="face front">
          {hasBack && (
            <button className="corner-btn flip-btn" aria-label="Flip for notes" onClick={flip}>
              <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" strokeWidth="2"><path d="M3 12a9 9 0 019-9c3.4 0 6.3 1.8 8 4.5M21 3v5h-5M21 12a9 9 0 01-9 9c-3.4 0-6.3-1.8-8-4.5M3 21v-5h5"/></svg>
            </button>
          )}
          {onToggleFav && (
            <button className={`corner-btn heart-btn${mine ? ' loved' : ''}`} aria-label="Heart this recipe" onClick={heart}>
              <IconHeart size={16} filled={mine} />
              {hearts.length > 0 && <span className="heart-ct">{hearts.length}</span>}
            </button>
          )}
          {lined ? (
            <div className="v-img v-lined-face">
              <p className="v-hand-note">{recipe.description ? recipe.description.slice(0, 110) : recipe.name}</p>
            </div>
          ) : (
            <div className="v-img" style={{ backgroundImage: `url(${img})` }}>
              {isQuickAndEasy(recipe) && <span className="card-quick" title="Quick &amp; Easy">⚡</span>}
            </div>
          )}
          <p className="v-no">No. {no}{recipe.category ? ` · ${recipe.category}` : ''}</p>
          <h3 className="v-title">{recipe.name}</h3>
          <p className="v-by">
            {mark && <AvatarMark avatar={mark} name={recipe.addedBy} size={16} className="v-by-mark" />}
            {recipe.addedBy ? `${recipe.addedBy}'s` : (recipe.author ? `by ${recipe.author}` : '')}
            {time ? ` · ${time}` : ''}
            {recipe.servings ? ` · ${recipe.servings}` : ''}
          </p>
          {!lined && recipe.description && <p className="v-note">"{recipe.description.slice(0, 80)}{recipe.description.length > 80 ? '…' : ''}"</p>}
        </div>
        <div className="face back" onClick={flip}>
          <button className="corner-btn flip-btn" aria-label="Flip back" onClick={flip}>
            <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" strokeWidth="2"><path d="M3 12a9 9 0 019-9c3.4 0 6.3 1.8 8 4.5M21 3v5h-5M21 12a9 9 0 01-9 9c-3.4 0-6.3-1.8-8-4.5M3 21v-5h5"/></svg>
          </button>
          <p className="vb-head">{recipe.name}</p>
          <p className="vb-sub">notes &amp; comments</p>
          <div className="vb-notes">
            {notes.map((n, i) => (
              <p key={i} className="vb-n"><span className="who">{n.who}:</span> {n.text}</p>
            ))}
          </div>
          <button className="vb-open" onClick={(e) => { e.stopPropagation(); onClick(); }}>full recipe →</button>
        </div>
      </div>
    </article>
  );
}

function MiniCard({ recipe, onClick }) {
  const img = recipe.image || '';
  return (
    <button className="mini-card" onClick={onClick}>
      <div className="mini-image" style={img ? { backgroundImage: `url(${img})` } : {}}>
        {!img && <IconBook size={22} stroke={1.2} />}
      </div>
      <div className="mini-body">
        {recipe.category && <div className="mini-cat">{recipe.category}</div>}
        <div className="mini-title">{recipe.name}</div>
        {(recipe.addedBy || recipe.author)
          ? <div className="mini-meta"><IconBook size={11} stroke={1.8} /> {recipe.addedBy || recipe.author}</div>
          : <div className="mini-meta"><IconClock size={12} /> {recipe.prep || recipe.cook || '—'}</div>}
      </div>
    </button>
  );
}

Object.assign(window, { Header, HomeView, RecipeCard, MiniCard, Stars, Pill, MetaStat, FamilyPortrait, MEAL_TYPES, MEAT_TYPES, avgRating, parseMinutes, isQuickAndEasy });
