// components.jsx — Book Journal components

const Stars = ({ rating, max = 5 }) =>
<span className="stars" aria-label={`${rating} von ${max} Sternen`}>
    {Array.from({ length: max }).map((_, i) =>
  <span key={i} className={i < rating ? '' : 'empty'}>★</span>
  )}
  </span>;


const Reveal = ({ children, delay = 0, as: Tag = 'div', ...rest }) => {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current;if (!el) return;
    const obs = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) {
        setTimeout(() => el.classList.add('in'), delay);
        obs.disconnect();
      }
    }, { threshold: 0.12, rootMargin: '0px 0px -40px 0px' });
    obs.observe(el);
    return () => obs.disconnect();
  }, [delay]);
  return <Tag ref={ref} className={`reveal ${rest.className || ''}`} {...rest}>{children}</Tag>;
};

const Ornament = ({ size = 80 }) =>
<svg width={size} height={size * 0.4} viewBox="0 0 100 40" fill="none" style={{ display: 'block' }}>
    <path d="M2 20 L42 20 M58 20 L98 20" stroke="currentColor" strokeWidth="0.7" />
    <circle cx="50" cy="20" r="3" stroke="currentColor" strokeWidth="0.7" fill="none" />
    <circle cx="50" cy="20" r="1" fill="currentColor" />
    <path d="M42 20 Q46 16 50 20 Q54 24 58 20" stroke="currentColor" strokeWidth="0.7" fill="none" />
    <path d="M42 20 Q46 24 50 20 Q54 16 58 20" stroke="currentColor" strokeWidth="0.7" fill="none" />
  </svg>;


const BookCover = ({ book, hero, photoId, photoPlaceholder }) => {
  if (photoId) {
    return (
      <div className="book-cover book-cover-photo">
        <image-slot
          id={photoId}
          shape="rect"
          fit="cover"
          placeholder={photoPlaceholder || 'Cover hierher ziehen'}
          style={{ width: '100%', height: '100%', display: 'block' }}>
        </image-slot>
      </div>
    );
  }
  return (
    <div className="book-cover book-cover-empty">
      <div className="book-cover-empty-inner">
        <svg width="34" height="34" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1" opacity="0.4">
          <rect x="3" y="5" width="18" height="14" rx="1" />
          <circle cx="12" cy="12" r="3" />
          <path d="M8 5l2-2h4l2 2" />
        </svg>
        <div className="book-cover-empty-label">Foto</div>
      </div>
    </div>
  );
};

const Hearts = ({ value, max = 5 }) =>
<span className="hearts" aria-label={`${value} von ${max} Herzen`}>
    {Array.from({ length: max }).map((_, i) =>
  <span key={i} className={i < value ? '' : 'empty'}>♥</span>
  )}
  </span>;


const RatingBreakdown = ({ ratings, overall }) =>
<div className="rating-card">
    <div className="rating-card-title">Bewertung</div>
    {ratings.map((r) =>
  <div key={r.label} className="rating-row">
        <div className="rating-row-label">{r.label}</div>
        <Hearts value={r.value} />
      </div>
  )}
    {overall != null &&
  <div className="rating-overall">
        <Stars rating={overall} />
      </div>
  }
  </div>;


const BookCard = ({ book, hero, currentRead, photoId, photoPlaceholder }) => {
  return (
    <Reveal as="article" className={`book-card ${hero ? 'hero-card' : ''} ${currentRead ? 'current-read' : ''}`}>
      {currentRead &&
      <div style={{ textAlign: 'center', marginBottom: 18 }}>
          <div className="current-read-label">Current Read</div>
          <div className="current-read-sub">Was ich gerade lese</div>
          <div className="ornament-divider" style={{ maxWidth: 200, margin: '0 auto 8px' }}>
            <Ornament size={50} />
          </div>
        </div>
      }
      <BookCover book={book} hero={hero} photoId={photoId} photoPlaceholder={photoPlaceholder} />
    </Reveal>);

};

const Header = ({ page, setPage, setNewsCategory }) => {
  const [openDropdown, setOpenDropdown] = React.useState(false);
  const dropdownRef = React.useRef(null);
  const newsCategories = ['Weekly Newcomer', 'Buchverfilmungen', 'Bestseller'];

  React.useEffect(() => {
    const onDoc = (e) => {
      if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
        setOpenDropdown(false);
      }
    };
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
  }, []);

  const [openCatDropdown, setOpenCatDropdown] = React.useState(false);
  const catDropdownRef = React.useRef(null);
  const categories = [
    { label: 'Neuzugänge', teaser: 'Frisch eingezogen: Welche Bücher neu auf meinem Stapel gelandet sind und warum ich einfach nicht an ihnen vorbei gehen konnte.' },
    { label: 'Bücher nach Vibe', teaserList: [
      { label: 'Sonne, Strand & Pageturner', desc: 'Egal ob prickelnde Urlaubsromance fürs Handtuch oder nervenaufreibende Romantic Suspense, bei der die Luft vor Hitze und Spannung flirrt — hier findest du Geschichten, die nach Sommer schmecken.' },
      { label: 'Sturmwetter & Leseglück', desc: 'Wenn der Regen gegen die Scheibe peitscht, schlägt die Stunde dieser Bücher. Ein gemütlicher Mix aus herzerwärmenden Wohlfühlromanen und tiefen, atmosphärischen Geschichten.' },
      { label: 'Gänsehaut & Schlaflose Nächte', desc: 'Vorsicht, akute Suchtgefahr! Hier lauern die Bücher, die dir eine wohlige Gänsehaut verpassen und dich mit dem Gedanken „Nur noch ein Kapitel!" die ganze Nacht wachhalten.' },
      { label: 'Zeitreisen & Große Klassiker', desc: 'Manchmal muss es eben eine Flucht aus dem Hier und Jetzt sein. Begleite mich in andere Epochen, vergangene Jahrhunderte und zu den zeitlosen Klassikern der Weltliteratur.' }
    ] },
    { label: 'Was nach dem Lesen bleibt', teaser: 'Gefühl statt Bewertung. Hier schreibe ich über Bücher, die mich auch nach dem Zuklappen nicht loslassen. Keine klassischen Kritiken, sondern der ganz persönliche Nachhall: Was hat mich bewegt, was hat mich inspiriert, was bleibt?' },
    { label: 'Oldies but Goldies', teaser: 'Schätze aus dem Bücherregal: Gute Geschichten haben kein Verfallsdatum! Hier findest du Bücher, die schon vor längerer Zeit erschienen sind, aber immer noch absolute Herzensempfehlungen sind. Keine verstaubten Klassiker, sondern einfach verdammt gute Geschichten, die es wert sind, heute (noch mal) gelesen zu werden.' }
  ];

  React.useEffect(() => {
    const onDoc = (e) => {
      if (catDropdownRef.current && !catDropdownRef.current.contains(e.target)) {
        setOpenCatDropdown(false);
      }
    };
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
  }, []);
  return (
    <header className="site-header">
      <div className="header-inner">
        <nav className="header-left">
          <div className={`nav-dropdown ${openDropdown ? 'open' : ''}`} ref={dropdownRef}>
            <a href="#" className={`nav-dropdown-trigger ${page === 'home' ? 'active' : ''}`}
            onClick={(e) => {e.preventDefault();setOpenDropdown((o) => !o);}}>BUCHWELT-NEWS

            </a>
            <div className="nav-dropdown-menu">
              {newsCategories.map((cat) =>
              <a key={cat} href="#" onClick={(e) => {
                e.preventDefault();
                setNewsCategory && setNewsCategory(cat);
                setPage('category');
                setOpenDropdown(false);
              }}>{cat}</a>
              )}
            </div>
          </div>
          <a href="#" className={page === 'blog' ? 'active' : ''}
             onClick={(e) => { e.preventDefault(); setPage('blog'); }}>
            Buchblog
          </a>
        </nav>
        <div className="brand" onClick={() => setPage('home')}>
          My life is all <span className="brand-book">Book</span><span className="brand-heart" aria-hidden="true"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 21s-7.5-4.6-9.5-9.4C1 7.6 4 4 7.5 4c2 0 3.5 1.1 4.5 2.6C13 5.1 14.5 4 16.5 4 20 4 23 7.6 21.5 11.6 19.5 16.4 12 21 12 21z"/></svg></span>ed
          <small>Book Journal</small>
        </div>
        <nav className="header-right">
          <a href="#" className={page === 'about' ? 'active' : ''}
             onClick={(e) => { e.preventDefault(); setPage('about'); }}>
            Über mich
          </a>
          <a href="#" className={page === 'contact' ? 'active' : ''}
             onClick={(e) => { e.preventDefault(); setPage('contact'); }}>
            Kontakt
          </a>
        </nav>
      </div>
      <div className="header-categories">
        <div className="header-categories-inner">
          {categories.map(cat => {
            const handlePick = (label) => {
              setNewsCategory && setNewsCategory(label);
              const quotes = window.CATEGORY_QUOTES || {};
              if (quotes[label] || label === 'Bücher nach Vibe') {
                setPage('category');
              } else {
                setPage('home');
              }
            };
            // Simple categories or single-teaser → render as <a>
            if (!cat.teaserList) {
              return (
                <a key={cat.label} href="#"
                  className={cat.teaser ? 'has-teaser' : ''}
                  onClick={(e) => { e.preventDefault(); handlePick(cat.label); }}>
                  {cat.label}
                  {cat.teaser && <span className="cat-tooltip" role="tooltip">{cat.teaser}</span>}
                </a>
              );
            }
            // Vibe category: wrapper hosts the tooltip with clickable sub-items
            return (
              <span key={cat.label} className="cat-wrap has-teaser">
                <a href="#" onClick={(e) => { e.preventDefault(); handlePick(cat.label); }}>{cat.label}</a>
                <span className="cat-tooltip cat-tooltip--list" role="tooltip">
                  <ul className="cat-tooltip-list">
                    {cat.teaserList.map(item => (
                      <li key={item.label}>
                        <a href="#" className="cat-tooltip-item"
                          onClick={(e) => { e.preventDefault(); handlePick(item.label); }}>
                          <span className="cat-tooltip-item-label">{item.label}</span>
                          <span className="cat-tooltip-item-desc">{item.desc}</span>
                        </a>
                      </li>
                    ))}
                  </ul>
                </span>
              </span>
            );
          })}
        </div>
      </div>
    </header>);
};

const Footer = () =>
<footer className="site-footer">
    <div className="brand-foot">My life is all <span className="brand-book">Book</span><span className="brand-heart" aria-hidden="true"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 21s-7.5-4.6-9.5-9.4C1 7.6 4 4 7.5 4c2 0 3.5 1.1 4.5 2.6C13 5.1 14.5 4 16.5 4 20 4 23 7.6 21.5 11.6 19.5 16.4 12 21 12 21z"/></svg></span>ed<small>Book Journal</small></div>
    <p>„Ein Buch ist ein Garten, den man in der Tasche trägt."</p>
    <div className="ornament-divider" style={{ color: 'rgba(245,239,224,0.4)' }}>
      <Ornament size={60} />
    </div>
    <div className="copyright">© 2026 · My life is all BOOKed</div>
  </footer>;


const HorizontalCarousel = ({ title, subtitle, books, onBookClick }) => {
  const trackRef = React.useRef(null);
  const scrollBy = (dir) => {
    const el = trackRef.current;if (!el) return;
    el.scrollBy({ left: dir * 280, behavior: 'smooth' });
  };
  return (
    <section className="h-carousel-wrap">
      <Reveal>
        <div className="h-carousel-head">
          <h2>
            <small>{subtitle}</small>
            {title}
          </h2>
          <div className="h-carousel-controls">
            <button className="h-carousel-btn" onClick={() => scrollBy(-1)} aria-label="Zurück">‹</button>
            <button className="h-carousel-btn" onClick={() => scrollBy(1)} aria-label="Weiter">›</button>
          </div>
        </div>
      </Reveal>
      <div className="h-carousel-track" ref={trackRef}>
        {books.map((b) =>
        <div className="h-carousel-item" key={b.id}>
            <BookCard book={b}
              photoId={onBookClick ? `cover-${b.id}` : undefined}
              photoPlaceholder="Bild ablegen" />
            {onBookClick &&
            <button type="button" className="h-carousel-details"
              onClick={() => onBookClick(b)}>Details ansehen</button>
            }
          </div>
        )}
      </div>
    </section>);

};

const OpenBookBanner = () =>
<Reveal as="section" className="book-banner">
    <svg className="banner-book" viewBox="0 0 1600 400" preserveAspectRatio="xMidYMid slice" xmlns="http://www.w3.org/2000/svg">
      <defs>
        <linearGradient id="pageGrad" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor="#f7eed8" stopOpacity="0.15" />
          <stop offset="50%" stopColor="#e8dcb8" stopOpacity="0.22" />
          <stop offset="100%" stopColor="#c9a961" stopOpacity="0.10" />
        </linearGradient>
        <linearGradient id="pageShadow" x1="0" y1="0" x2="1" y2="0">
          <stop offset="0%" stopColor="#000" stopOpacity="0" />
          <stop offset="48%" stopColor="#000" stopOpacity="0.55" />
          <stop offset="50%" stopColor="#000" stopOpacity="0.7" />
          <stop offset="52%" stopColor="#000" stopOpacity="0.55" />
          <stop offset="100%" stopColor="#000" stopOpacity="0" />
        </linearGradient>
        <radialGradient id="lightGlow" cx="50%" cy="20%" r="60%">
          <stop offset="0%" stopColor="#ffe7a8" stopOpacity="0.25" />
          <stop offset="100%" stopColor="#ffe7a8" stopOpacity="0" />
        </radialGradient>
      </defs>
      {/* warm light glow from above */}
      <rect width="1600" height="400" fill="url(#lightGlow)" />
      {/* left page */}
      <path d="M 200,80 Q 200,60 250,55 L 780,40 Q 800,40 800,60 L 800,360 Q 800,380 780,378 L 250,365 Q 200,360 200,340 Z"
    fill="url(#pageGrad)" stroke="#c9a961" strokeWidth="1" strokeOpacity="0.5" />
      {/* right page */}
      <path d="M 1400,80 Q 1400,60 1350,55 L 820,40 Q 800,40 800,60 L 800,360 Q 800,380 820,378 L 1350,365 Q 1400,360 1400,340 Z"
    fill="url(#pageGrad)" stroke="#c9a961" strokeWidth="1" strokeOpacity="0.5" />
      {/* spine shadow */}
      <rect x="780" y="40" width="40" height="338" fill="url(#pageShadow)" />
      {/* text lines on left page */}
      {Array.from({ length: 14 }).map((_, i) =>
    <line key={`l-${i}`}
    x1={240 + i * 1.5} y1={90 + i * 18}
    x2={760 - i * 0.5} y2={90 + i * 18}
    stroke="#c9a961" strokeOpacity={i === 0 ? 0.6 : 0.28} strokeWidth={i === 0 ? 1.2 : 0.7}
    strokeDasharray={i === 0 ? "0" : i % 3 === 0 ? "180,60,90,40" : "120,30,200,40,90,30"} />
    )}
      {/* text lines on right page */}
      {Array.from({ length: 14 }).map((_, i) =>
    <line key={`r-${i}`}
    x1={840 + i * 0.5} y1={90 + i * 18}
    x2={1360 - i * 1.5} y2={90 + i * 18}
    stroke="#c9a961" strokeOpacity="0.28" strokeWidth="0.7"
    strokeDasharray={i === 6 ? "30" : i % 2 === 0 ? "150,40,180,30,90" : "120,30,160,40"} />
    )}
      {/* drop cap on left page */}
      <text x="246" y="120" fontFamily="Cormorant Garamond, serif" fontStyle="italic" fontSize="42" fill="#c9a961" fillOpacity="0.7">K</text>
      {/* page corners curl */}
      <path d="M 1380,360 Q 1395,355 1400,340 L 1400,355 Q 1395,365 1380,360 Z" fill="#9c7f3f" fillOpacity="0.35" />
      <path d="M 220,360 Q 205,355 200,340 L 200,355 Q 205,365 220,360 Z" fill="#9c7f3f" fillOpacity="0.35" />
      {/* subtle ribbon bookmark */}
      <path d="M 1100,40 L 1100,180 L 1110,165 L 1120,180 L 1120,40 Z" fill="#8b1e1e" fillOpacity="0.6" />
    </svg>
    <div className="book-banner-overlay">
      <div className="quote">„Ein Buch ist ein Garten, den man in der Tasche trägt."</div>
      <div className="quote-author">— Arabisches Sprichwort</div>
    </div>
  </Reveal>;


const BookSpine = ({ book, height, onClick }) => {
  // Dark navy spines — matching the header bar; subtle variation so the row reads as individual books
  const palettes = [
    'linear-gradient(180deg, #1f2c44, #0d1626)',
    'linear-gradient(180deg, #243450, #101a2c)',
    'linear-gradient(180deg, #1a2740, #0b1322)',
    'linear-gradient(180deg, #28384f, #121d30)',
    'linear-gradient(180deg, #1d2a42, #0e1726)',
    'linear-gradient(180deg, #223150, #0f1928)',
    'linear-gradient(180deg, #1b2840, #0c1524)',
    'linear-gradient(180deg, #26364e, #111b2e)',
    'linear-gradient(180deg, #1f2e48, #0d1726)',
    'linear-gradient(180deg, #213152, #0f1a2c)',
    'linear-gradient(180deg, #1c2a44, #0b1422)',
    'linear-gradient(180deg, #253651, #121c2f)',
  ];
  const hash = (book?.id || '').split('').reduce((a, c) => a + c.charCodeAt(0), 0);
  const bg = palettes[hash % palettes.length];
  const tlen = (book?.title || '').length;
  const titleSize = tlen > 34 ? 9.5 : tlen > 26 ? 11 : 12.5;
  return (
    <div className="shelf-book"
         style={{ '--h': `${height}px`, background: bg }}
         title={book?.title || 'Buch'}
         onClick={() => onClick && onClick(book)}>
      <span className="spine-label">
        <span className="spine-title" style={{ fontSize: `${titleSize}px` }}>{book?.title || ''}</span>
        {book?.author && <span className="spine-author">{book.author}</span>}
      </span>
    </div>
  );
};

const EmptySpine = ({ height, idx }) => {
  const palettes = [
    'linear-gradient(180deg, #19243a, #0a121f)',
    'linear-gradient(180deg, #1d2942, #0c1422)',
    'linear-gradient(180deg, #162136, #0a111d)',
    'linear-gradient(180deg, #1b2740, #0b1320)',
    'linear-gradient(180deg, #1e2b44, #0d1624)',
    'linear-gradient(180deg, #182338, #0a111e)',
  ];
  const bg = palettes[idx % palettes.length];
  return (
    <div className="shelf-book empty" style={{ '--h': `${height}px`, background: bg }}>
      <span className="spine-label">leer</span>
    </div>
  );
};

const Bookshelf = ({ books, onBookClick }) => {
  const PER_ROW = 9;
  // Group by genre, preserving series order within each genre.
  const byGenre = {};
  const genreOrder = [];
  [...books].forEach((b) => {
    const g = b.genre || 'Sonstiges';
    if (!byGenre[g]) { byGenre[g] = []; genreOrder.push(g); }
    byGenre[g].push(b);
  });
  genreOrder.forEach((g) => {
    byGenre[g].sort((a, b) =>
      (a.series || a.author || '').localeCompare(b.series || b.author || '', 'de') ||
      ((a.seriesIndex ?? 99) - (b.seriesIndex ?? 99)) ||
      a.title.localeCompare(b.title, 'de')
    );
  });

  const chunk = (arr) => {
    const rows = [];
    for (let i = 0; i < arr.length; i += PER_ROW) rows.push(arr.slice(i, i + PER_ROW));
    return rows;
  };

  return (
    <div className="bookshelf">
      {genreOrder.map((g, gi) => {
        const list = byGenre[g];
        // Pad the final row out to a full shelf with empty spines.
        const padCount = (PER_ROW - (list.length % PER_ROW)) % PER_ROW;
        const padded = [
          ...list,
          ...Array.from({ length: padCount }).map((_, i) => ({ empty: true, idx: gi * 100 + list.length + i })),
        ];
        const rows = chunk(padded);
        return (
          <div className="shelf-genre" key={g}>
            <div className="shelf-genre-label">{g}</div>
            {rows.map((row, ri) => (
              <div className="bookshelf-row" key={ri}>
                {row.map((b, bi) => {
                  const height = 184 + (((bi + gi) * 13 + ri * 7) % 5) * 8;
                  return b.empty
                    ? <EmptySpine key={`e-${gi}-${ri}-${bi}`} height={height} idx={b.idx} />
                    : <BookSpine key={b.id || `${gi}-${ri}-${bi}`} book={b} height={height} onClick={onBookClick} />;
                })}
              </div>
            ))}
          </div>
        );
      })}
    </div>
  );
};

const BookDetailModal = ({ book, onClose }) => {
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    if (book) {
      document.addEventListener('keydown', onKey);
      document.body.style.overflow = 'hidden';
    }
    return () => {
      document.removeEventListener('keydown', onKey);
      document.body.style.overflow = '';
    };
  }, [book, onClose]);

  if (!book) return null;

  const summary = book.summary || book.excerpt || '';
  return (
    <div className="book-modal-backdrop" onClick={onClose}>
      <div className="book-modal" role="dialog" aria-modal="true" onClick={(e) => e.stopPropagation()}>
        <button className="book-modal-close" aria-label="Schließen" onClick={onClose}>×</button>
        <div className="book-modal-cover">
          <div className="book-modal-cover-orn book-modal-cover-orn-top"><Ornament size={46} /></div>
          <div className="book-modal-cover-frame">
            <image-slot
              id={`cover-${book.id}`}
              shape="rect"
              fit="cover"
              placeholder="Cover hierher ziehen"
              style={{ width: '100%', height: '100%', display: 'block' }}>
            </image-slot>
          </div>
          <div className="book-modal-cover-orn book-modal-cover-orn-bottom"><Ornament size={46} /></div>
        </div>
        <div className="book-modal-body">
          <div className="book-modal-genre">{book.genre}{(() => {
            if (!book.series || !book.seriesIndex) return '';
            const inSeries = (window.BOOKS || []).filter((b) => b.series === book.series).length;
            return inSeries > 1 ? ` · Band ${book.seriesIndex}` : '';
          })()}</div>
          <h2 className="book-modal-title">{book.title}</h2>
          <div className="book-modal-author">{book.author}</div>
          <div className="ornament-divider" style={{ maxWidth: 220, margin: '14px 0 18px' }}>
            <Ornament size={52} />
          </div>
          {book.summary && <div className="book-modal-summary-label">Darum geht's</div>}
          <p className="book-modal-summary">{summary}</p>
          <div className="book-modal-meta">
            {book.pages && <span><strong>{book.pages}</strong> Seiten</span>}
            {book.series && (window.BOOKS || []).filter((b) => b.series === book.series).length > 1 &&
              <span>Reihe: <strong>{book.series}</strong></span>}
          </div>
        </div>
      </div>
    </div>
  );
};

const BlogPostModal = ({ post, onClose }) => {
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    if (post) {
      document.addEventListener('keydown', onKey);
      document.body.style.overflow = 'hidden';
    }
    return () => {
      document.removeEventListener('keydown', onKey);
      document.body.style.overflow = '';
    };
  }, [post, onClose]);

  if (!post) return null;

  const d = new Date(post.date);
  const dateStr = isNaN(d) ? post.date
    : d.toLocaleDateString('de-DE', { day: '2-digit', month: 'long', year: 'numeric' });

  const body = post.body || [{ type: 'para', text: post.excerpt }];

  return (
    <div className="book-modal-backdrop" onClick={onClose}>
      <div className="post-modal" role="dialog" aria-modal="true" onClick={(e) => e.stopPropagation()}>
        <button className="book-modal-close" aria-label="Schließen" onClick={onClose}>×</button>
        <div className="post-modal-scroll">
          <header className="post-modal-head">
            <div className="post-modal-cat">{post.category || 'Was nach dem Lesen bleibt'}</div>
            {post.section && <div className="post-modal-section">{post.section}</div>}
            <h1 className={`post-modal-title${post.section ? ' compact' : ''}`}>{post.title}</h1>
            <div className="post-modal-meta">
              <span>{dateStr}</span>
              {post.readTime && <span>·</span>}
              {post.readTime && <span>{post.readTime} Lesezeit</span>}
            </div>
            <div className="ornament-divider" style={{ maxWidth: 240, margin: '20px auto 0' }}>
              <Ornament size={52} />
            </div>
          </header>
          {post.cover && (
            <div className="post-modal-cover-photo">
              <image-slot
                id={`post-hero-${post.id}`}
                shape="rect"
                fit="cover"
                placeholder="Foto hierher ziehen"
                style={{ width: '100%', height: '100%', display: 'block' }}>
              </image-slot>
            </div>
          )}
          <div className="post-modal-body">
            {body.map((block, i) => {
              if (block.type === 'subhead') return <h2 key={i} className="post-block-subhead">{block.text}</h2>;
              if (block.type === 'pull') return <blockquote key={i} className="post-block-pull">{block.text}</blockquote>;
              if (block.type === 'signoff') return <p key={i} className="post-block-signoff">{block.text}</p>;
              if (block.type === 'signature') return <div key={i} className="post-block-signature">{block.text}</div>;
              if (block.type === 'facts') return (
                <div key={i} className="post-block-facts">
                  <div className="post-facts-photo">
                    <image-slot
                      id={`post-cover-${post.id}`}
                      shape="rect"
                      fit="cover"
                      placeholder="Cover hierher ziehen"
                      style={{ width: '100%', height: '100%', display: 'block' }}>
                    </image-slot>
                  </div>
                  <dl className="post-facts-list">
                    {block.items.map(([k, v], j) => (
                      <div className="post-fact-row" key={j}>
                        <dt>{k}</dt>
                        <dd>{v}</dd>
                      </div>
                    ))}
                  </dl>
                </div>
              );
              if (block.type === 'note') return (
                <p key={i} className="post-block-note"><strong>{block.label}:</strong> {block.text}</p>
              );
              return <p key={i} className="post-block-para">{block.text}</p>;
            })}
          </div>
          <CommentSection postId={post.id} />
        </div>
      </div>
    </div>
  );
};

const CommentSection = ({ postId }) => {
  const storeKey = `blog-comments-${postId}`;
  const [comments, setComments] = React.useState([]);
  const [name, setName] = React.useState('');
  const [text, setText] = React.useState('');

  React.useEffect(() => {
    try {
      const raw = localStorage.getItem(storeKey);
      setComments(raw ? JSON.parse(raw) : []);
    } catch (e) { setComments([]); }
  }, [storeKey]);

  const save = (list) => {
    setComments(list);
    try { localStorage.setItem(storeKey, JSON.stringify(list)); } catch (e) {}
  };

  const submit = (e) => {
    e.preventDefault();
    const t = text.trim();
    if (!t) return;
    const entry = {
      id: Date.now(),
      name: name.trim() || 'Anonym',
      text: t,
      date: new Date().toISOString(),
    };
    save([...comments, entry]);
    setText('');
  };

  const fmt = (iso) => {
    const d = new Date(iso);
    return isNaN(d) ? '' : d.toLocaleDateString('de-DE', { day: '2-digit', month: 'long', year: 'numeric' });
  };

  return (
    <section className="comments">
      <div className="ornament-divider" style={{ maxWidth: 200, margin: '36px auto 24px' }}>
        <Ornament size={48} />
      </div>
      <h3 className="comments-title">
        {comments.length > 0 ? `Kommentare (${comments.length})` : 'Schreib den ersten Kommentar'}
      </h3>

      {comments.length > 0 && (
        <ul className="comments-list">
          {comments.map((c) => (
            <li className="comment" key={c.id}>
              <div className="comment-head">
                <span className="comment-name">{c.name}</span>
                <span className="comment-date">{fmt(c.date)}</span>
              </div>
              <p className="comment-text">{c.text}</p>
            </li>
          ))}
        </ul>
      )}

      <form className="comment-form" onSubmit={submit}>
        <input
          className="comment-input"
          type="text"
          placeholder="Dein Name (optional)"
          value={name}
          onChange={(e) => setName(e.target.value)} />
        <textarea
          className="comment-textarea"
          placeholder="Teile deine Gedanken zu diesem Buch …"
          rows={3}
          value={text}
          onChange={(e) => setText(e.target.value)}></textarea>
        <button type="submit" className="comment-submit">Kommentar absenden</button>
      </form>
    </section>
  );
};

Object.assign(window, { Stars, Hearts, RatingBreakdown, Reveal, Ornament, BookCover, BookCard, BookDetailModal, BlogPostModal, CommentSection, Header, Footer, HorizontalCarousel, OpenBookBanner, Bookshelf, BookSpine, EmptySpine });