/* sections.jsx — page sections for La Signora */

const { useState, useEffect, useLayoutEffect, useRef } = React;

// ── Reveal-on-scroll wrapper ──
function Reveal({ children, as: As = 'div', className = '', ...rest }) {
  const ref = useRef(null);
  const [shown, setShown] = useState(false);
  useLayoutEffect(() => {
    const el = ref.current;
    if (!el) return;
    // Already above the fold — show before first paint, no flash
    if (el.getBoundingClientRect().top < window.innerHeight) {
      setShown(true);
      return;
    }
    const io = new IntersectionObserver(
      ([e]) => { if (e.isIntersecting) { setShown(true); io.disconnect(); } },
      { threshold: 0.12, rootMargin: '0px 0px -80px 0px' }
    );
    io.observe(el);
    return () => io.disconnect();
  }, []);
  return (
    <As ref={ref} className={`reveal ${shown ? 'in' : ''} ${className}`} {...rest}>
      {children}
    </As>
  );
}

// ── Page detection (used by Nav and App) ──
function getPage() {
  const p = window.location.pathname;
  if (/menu\.html/.test(p)) return 'menu';
  if (/find\.html/.test(p)) return 'find';
  if (/catering\.html/.test(p)) return 'catering';
  if (/contact\.html/.test(p)) return 'contact';
  if (/privacy\.html/.test(p)) return 'privacy';
  if (/accessibility\.html/.test(p)) return 'accessibility';
  return 'home';
}

// ── Nav ──
const NAV_ITEMS = [
  { href: 'index.html',    page: 'home',     label: 'Home',     hint: 'La Signora' },
  { href: 'menu.html',     page: 'menu',     label: 'Menu',     hint: 'Arancini & specials' },
  { href: 'catering.html', page: 'catering', label: 'Services', hint: 'Private + corporate' },
  { href: 'contact.html',  page: 'contact',  label: 'Contact',  hint: 'Get in touch' },
];

function Nav() {
  const [scrolled, setScrolled] = useState(false);
  const [menuOpen, setMenuOpen] = useState(false);
  const currentPage = getPage();

  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 24);
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, []);

  // Close drawer when resizing back to desktop
  useEffect(() => {
    const onResize = () => { if (window.innerWidth > 820) setMenuOpen(false); };
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);

  // Lock body scroll while drawer is open
  useEffect(() => {
    document.body.style.overflow = menuOpen ? 'hidden' : '';
    return () => { document.body.style.overflow = ''; };
  }, [menuOpen]);

  // Close drawer on Escape
  useEffect(() => {
    if (!menuOpen) return;
    const onKey = (e) => { if (e.key === 'Escape') setMenuOpen(false); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [menuOpen]);

  return (
    <>
      <header className={`nav ${scrolled ? 'scrolled' : ''}`}>
        <a className="nav-brand" href="index.html">
          <span className="mark"><img src="assets/mascot.png" alt="" /></span>
          <span>La <em>Signora</em></span>
        </a>

        <nav className="nav-links" aria-label="Site sections">
          {NAV_ITEMS.map((item) => (
            <a
              key={item.page}
              href={item.href}
              className={currentPage === item.page ? 'active' : ''}
              aria-current={currentPage === item.page ? 'page' : undefined}
              title={item.hint}
            >
              <span className="lbl">{item.label}</span>
            </a>
          ))}
          <a
            href="find.html"
            className={`pill ${currentPage === 'find' ? 'active' : ''}`}
          >
            <PinIcon size={14} color="currentColor" />
            Find Us
          </a>
        </nav>

        <button
          className={`nav-burger ${menuOpen ? 'open' : ''}`}
          onClick={() => setMenuOpen((o) => !o)}
          aria-label={menuOpen ? 'Close menu' : 'Open menu'}
          aria-expanded={menuOpen}
        >
          <span /><span /><span />
        </button>
      </header>

      {/* Mobile drawer — slides down from top, sits below the sticky nav */}
      <div
        className={`nav-mobile ${menuOpen ? 'open' : ''}`}
        aria-hidden={!menuOpen}
      >
        {NAV_ITEMS.map((item) => (
          <a
            key={item.page}
            href={item.href}
            className={currentPage === item.page ? 'active' : ''}
            tabIndex={menuOpen ? undefined : -1}
          >
            {item.label}
          </a>
        ))}
        <a
          href="find.html"
          className={`pill ${currentPage === 'find' ? 'active' : ''}`}
          tabIndex={menuOpen ? undefined : -1}
        >
          <PinIcon size={14} color="currentColor" />
          Find Us
        </a>
      </div>
    </>
  );
}

// ── Hero ──
function Hero() {
  return (
    <section className="hero" id="top">
      <div className="hero-tile"></div>

      <div className="hero-content">
        <div className="eyebrow">Sicilian street food · Portland, OR</div>
        <h1 className="display">
          <span className="script-overlay">made with love,</span>
          <br />
          From Sicily,<br />
          <span className="it">with my mother's</span><br />
          recipes.
        </h1>
        <p className="sub">
          Antonietta cooks the food she grew up on in Sicily — slow-braised
          ragù, hand-shaped arancini, the rotating specials her regulars come
          back for. No shortcuts, no compromises, and certainly no red-and-white
          checkered tablecloths.
        </p>
        <div className="ctas">
          <a href="find.html" className="btn btn-primary">
            Find Us <span className="arrow">→</span>
          </a>
          <a href="menu.html" className="btn btn-ghost">
            See the Menu
          </a>
          <a
            href="https://doordash.com/store/42863089"
            target="_blank"
            rel="noopener noreferrer"
            className="btn-saffron"
          >
            Order on DoorDash <span className="arrow"><ExternalArrowIcon /></span>
          </a>
        </div>
        <div className="scribble">
          <span className="star">✦</span>
          <span>Cathedral Park Food Carts · St. Johns</span>
        </div>
      </div>

      <div className="hero-portrait">
        <div className="frame">
          <div className="ring r2"></div>
          <div className="ring"></div>
          <div className="disc"></div>
          <img className="mascot" src="assets/mascot.png" alt="La Signora — the arancino mascot in a sun hat and red glasses" />
          <div className="stamp">
            Ciao!
            <small>EST. 2025</small>
          </div>
        </div>
      </div>
    </section>
  );
}

// ── Marquee strip ──
function Strip() {
  const phrases = [
    'Ragù di Mamma',
    'Prosciutto · Pistacchio',
    'Mozzarella · Funghi',
    'Pasta al Forno',
    'Cannoli',
    'Tiramisù',
    'Lasagna della Domenica'
  ];
  const items = (
    <span>
      {phrases.map((p, i) => (
        <React.Fragment key={i}>
          {p}
          <span className="dot"></span>
        </React.Fragment>
      ))}
    </span>
  );
  return (
    <div className="strip" aria-hidden="true">
      <div className="strip-track">
        {items}{items}{items}
      </div>
    </div>
  );
}

// ── Story ──
function Story() {
  return (
    <section className="story" id="story">
      <div className="wrap">
        <Divider />
        <div className="story-grid">
          <Reveal className="story-aside">
            <div className="eyebrow">The story</div>
            <h2>Meet<br /><em>la Signora.</em></h2>
            <p className="lede">
              The food has a name. So does the woman who makes it.
            </p>
            <div className="signoff">— Antonietta Benigno</div>
          </Reveal>

          <Reveal className="story-body">
            <p>
              Antonietta Benigno grew up in southern Sicily, where Sunday meant
              her mother at the stove and a kitchen full of people. She built a
              career on two continents — managing a restaurant in New Jersey,
              then teaching back in Italy — before crossing the ocean again in
              2021 to be near her daughter in Portland.
            </p>
            <p>
              She wasn't planning on cooking for a living. She was working as a
              manager here when an accident forced her to leave the job — so she
              started cooking the way her mother taught her instead, and people
              noticed. Her Spanish-speaking co-workers used to call her warmly{' '}
              <em>la señora</em> — she translated it into Italian, and made it
              the name above her cart.
            </p>

            <div className="pullquote">
              <p>I'm thinking about the people that will eat my food, and I try to put my heart into it — and they can feel it.</p>
              <div className="by">Antonietta · La Signora</div>
            </div>

            <p>
              Every dish is something her mother taught her: a four-hour ragù
              that can't be hurried, arancini shaped one by one, sweets she
              learned standing on a chair in a Sicilian kitchen. If she can't
              get the right ingredients — some still come from Italy — she
              won't put the dish on the board.
            </p>

            <div className="pullquote" style={{ background: 'rgba(224,166,56,.12)', borderLeftColor: 'var(--saffron)' }}>
              <p>Every time I make her recipes, it's like she's right there in the kitchen with me.</p>
            </div>

            <div className="journey">
              <span className="step">Sicily</span>
              <span className="arrow">→</span>
              <span className="step">New Jersey</span>
              <span className="arrow">→</span>
              <span className="step">Milan</span>
              <span className="arrow">→</span>
              <span className="step">Portland</span>
            </div>

            <p className="press-credit">
              As featured in the{' '}
              <a
                href="https://portlandtribune.com/2026/04/09/la-signora-food-truck-brings-sicilian-street-food-to-portlands-st-johns-neighborhood/"
                target="_blank"
                rel="noopener noreferrer"
              >
                Portland Tribune <ExternalArrowIcon size={12} />
              </a>
            </p>
          </Reveal>
        </div>
      </div>
    </section>
  );
}

// ── Menu ──
const FLAVORS = [
  {
    name: 'Carlentini',
    shape: 'Classico',
    city: 'Ragù bolognese',
    blurb: 'Slow-braised meat sauce from a four-hour recipe her mother taught her. The one she will not rush.',
    ings: ['Risotto', 'Angus beef', 'Tomato', 'Carrot', 'Onion', 'Celery', 'Peas', 'Mozzarella'],
    extras: [
      ['Vegetarian', 'No'],
      ['Allergens', 'Gluten, Dairy'],
    ],
    palette: ['#B5482B', '#8C341E', '#E0A638']
  },
  {
    name: 'Bronte',
    city: 'Prosciutto & Pistacchio',
    blurb: 'Salty cured pork, pistachios, mozzarella — the Sicilian flavor pairing that converts every skeptic.',
    ings: ['Risotto', 'Prosciutto', 'Pistachio cream', 'Mozzarella'],
    extras: [
      ['Vegetarian', 'No'],
      ['Allergens', 'Gluten, Dairy, Tree nuts'],
    ],
    palette: ['#5C6B33', '#3F4A21', '#E6D2A9'],
    hidden: true, // replaced by Mafalda — keeping the data around in case it comes back
  },
  {
    name: 'Mafalda',
    shape: 'Sferico',
    city: 'Pistacchio, Mortadella & Mozzarella',
    blurb: 'Mortadella and pistachio, mozzarella folded in — the classic Italian sandwich pairing, reshaped into a cone.',
    ings: ['Risotto', 'Mortadella', 'Pistachio cream', 'Mozzarella'],
    extras: [
      ['Vegetarian', 'No'],
      ['Allergens', 'Gluten, Dairy, Tree nuts'],
    ],
    palette: ['#5C6B33', '#3F4A21', '#E6D2A9']
  },
  {
    name: 'Madonie',
    shape: 'Allungato',
    city: 'Mozzarella & Funghi',
    blurb: 'Mushrooms slow-cooked in extra virgin olive oil, chopped and folded with low-moisture mozzarella. Vegetarian. Quietly the favorite.',
    ings: ['Risotto', 'Mixed mushrooms', 'Extra virgin olive oil', 'Mozzarella'],
    extras: [
      ['Vegetarian', 'Yes'],
      ['Allergens', 'Gluten, Dairy'],
    ],
    palette: ['#8C341E', '#5C6B33', '#E0A638']
  },
  {
    name: 'Siracusa',
    shape: 'Cilindrico',
    city: 'Spinaci & Mozzarella',
    blurb: 'Spinach sautéed in butter, folded with mozzarella. Simple, honest, and somehow the one people keep coming back to.',
    ings: ['Risotto', 'Spinach', 'Butter', 'Mozzarella'],
    extras: [
      ['Vegetarian', 'Yes'],
      ['Allergens', 'Gluten, Dairy'],
    ],
    palette: ['#5C6B33', '#3F4A21', '#E6D2A9']
  },
  {
    name: 'Norma',
    shape: 'Diamante',
    city: 'alla Catanese',
    blurb: 'Fried eggplant, tomato and salted ricotta — the Catania classic, reshaped into a cone.',
    ings: ['Risotto', 'Fried eggplant', 'Tomato', 'Ricotta salata'],
    extras: [
      ['Vegetarian', 'Yes'],
      ['Allergens', 'Gluten, Dairy'],
    ],
    palette: ['#8C341E', '#5C6B33', '#E0A638']
  },
];

const VISIBLE_FLAVORS = FLAVORS.filter((f) => !f.hidden);

function ArancinoCard({ f, idx, open, onToggle }) {
  return (
    <div className={`card ${open ? 'open' : ''}`} onClick={onToggle}>
      <div className="num">N° {String(idx + 1).padStart(2, '0')}</div>
      <ArancinoStamp palette={f.palette} />
      <h3 className="display">{f.name}</h3>
      <div className="city">{f.city}</div>
      <p className="blurb">{f.blurb}</p>
      <div className="ings">
        {f.ings.map((i, k) => <span key={k}>{i}</span>)}
      </div>
      <div className="details">
        {f.extras.map(([k, v], i) => (
          <div className="row" key={i}><span>{k}</span><b>{v}</b></div>
        ))}
      </div>
      <div className="more">
        {open ? 'Less' : 'More'} <span className="chev">▾</span>
      </div>
    </div>
  );
}

function ArancinoSlider({ flavors }) {
  const trackRef = useRef(null);
  const [open, setOpen] = useState(0);
  const [active, setActive] = useState(0);
  const [scrollLeft, setScrollLeft] = useState(0);
  const [maxScroll, setMaxScroll] = useState(0);

  // measure on mount + resize
  useEffect(() => {
    const el = trackRef.current;
    if (!el) return;
    const measure = () => setMaxScroll(el.scrollWidth - el.clientWidth);
    measure();
    const ro = new ResizeObserver(measure);
    ro.observe(el);
    return () => ro.disconnect();
  }, [flavors.length]);

  // active page = leftmost visible card index
  const onScroll = () => {
    const el = trackRef.current;
    if (!el) return;
    setScrollLeft(el.scrollLeft);
    const cards = el.querySelectorAll('.card');
    if (!cards.length) return;
    const left = el.scrollLeft;
    let bestI = 0, bestD = Infinity;
    cards.forEach((c, i) => {
      const d = Math.abs(c.offsetLeft - left);
      if (d < bestD) { bestD = d; bestI = i; }
    });
    setActive(bestI);
  };

  const scrollToCard = (i) => {
    const el = trackRef.current;
    if (!el) return;
    const card = el.querySelectorAll('.card')[i];
    if (!card) return;
    el.scrollTo({ left: card.offsetLeft, behavior: 'smooth' });
  };

  const step = (dir) => {
    const el = trackRef.current;
    if (!el) return;
    const cards = el.querySelectorAll('.card');
    const next = Math.max(0, Math.min(cards.length - 1, active + dir));
    scrollToCard(next);
  };

  const atStart = active <= 0;
  const atEnd = maxScroll > 0 && scrollLeft >= maxScroll - 4;

  return (
    <div className="slider">
      <div
        className="slider-track"
        ref={trackRef}
        onScroll={onScroll}
        role="region"
        aria-label="Arancini flavors"
      >
        {flavors.map((f, i) => (
          <ArancinoCard
            key={i}
            f={f}
            idx={i}
            open={open === i}
            onToggle={() => setOpen(open === i ? -1 : i)}
          />
        ))}
      </div>

      <div className="slider-controls">
        <div className="slider-dots" role="tablist" aria-label="Jump to flavor">
          {flavors.map((f, i) => (
            <button
              key={i}
              className={`dot ${active === i ? 'on' : ''}`}
              onClick={() => scrollToCard(i)}
              aria-label={`Go to ${f.name}`}
              aria-selected={active === i}
            />
          ))}
        </div>
        <div className="slider-arrows">
          <button
            className="arrow-btn"
            onClick={() => step(-1)}
            disabled={atStart}
            aria-label="Previous flavor"
          >←</button>
          <span className="slider-count">
            <em>{String(active + 1).padStart(2, '0')}</em>
            <span>/ {String(flavors.length).padStart(2, '0')}</span>
          </span>
          <button
            className="arrow-btn"
            onClick={() => step(1)}
            disabled={atEnd}
            aria-label="Next flavor"
          >→</button>
        </div>
      </div>
    </div>
  );
}

function Menu() {
  const specials = [
    { it: 'Lasagna della Domenica', en: 'Sunday lasagna', tag: 'Heritage' },
    { it: 'Pasta al Forno', en: 'Baked pasta', tag: 'Comfort' },
    { it: 'Cannoli', en: 'filled fresh when you order', tag: 'Dolce' },
    { it: 'Tiramisù', en: 'made the long way', tag: 'Dolce' },
    { it: 'Parmigiana di Melanzane', en: 'eggplant parmigiana', tag: 'When she finds them' },
  ];

  const [lightbox, setLightbox] = useState(null);
  const lbOuterRef = useRef(null);
  const lastFocusedRef = useRef(null);

  useEffect(() => {
    if (!lightbox) return;

    // Remember what had focus so it can be restored on close, then move
    // focus into the dialog (close button) for keyboard/screen-reader users.
    lastFocusedRef.current = document.activeElement;
    const closeBtn = lbOuterRef.current && lbOuterRef.current.querySelector('.lb-close');
    if (closeBtn) closeBtn.focus();

    const onKey = (e) => {
      if (e.key === 'Escape') { setLightbox(null); return; }
      if (e.key !== 'Tab' || !lbOuterRef.current) return;
      // Trap Tab focus inside the dialog while it's open.
      const focusable = lbOuterRef.current.querySelectorAll(
        'a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])'
      );
      if (focusable.length === 0) return;
      const first = focusable[0];
      const last = focusable[focusable.length - 1];
      if (e.shiftKey && document.activeElement === first) {
        e.preventDefault();
        last.focus();
      } else if (!e.shiftKey && document.activeElement === last) {
        e.preventDefault();
        first.focus();
      }
    };
    document.addEventListener('keydown', onKey);
    document.body.style.overflow = 'hidden';
    return () => {
      document.removeEventListener('keydown', onKey);
      document.body.style.overflow = '';
      if (lastFocusedRef.current) lastFocusedRef.current.focus();
    };
  }, [lightbox]);

  const navigate = (dir) => {
    const idx = VISIBLE_FLAVORS.indexOf(lightbox);
    setLightbox(VISIBLE_FLAVORS[(idx + dir + VISIBLE_FLAVORS.length) % VISIBLE_FLAVORS.length]);
  };

  const touchStartRef = useRef(null);
  const onLbTouchStart = (e) => {
    const t = e.touches[0];
    touchStartRef.current = { x: t.clientX, y: t.clientY };
  };
  const onLbTouchEnd = (e) => {
    const start = touchStartRef.current;
    touchStartRef.current = null;
    if (!start) return;
    const t = e.changedTouches[0];
    const dx = t.clientX - start.x;
    const dy = t.clientY - start.y;
    if (Math.abs(dx) > 40 && Math.abs(dx) > Math.abs(dy) * 1.5) {
      navigate(dx < 0 ? 1 : -1);
    }
  };

  return (
    <React.Fragment>
    <section className="menu" id="menu">
      <div className="wrap">
        <Divider />
        <Reveal className="menu-head">
          <div>
            <div className="eyebrow">The menu</div>
            <h2>Dal Cuore della<br /><em>Sicilia.</em></h2>
          </div>
          <div className="note">
            Arancini are the heart of the cart — shaped by hand and named after
            the regions they come from. The specials change with what she can
            source.
          </div>
        </Reveal>

        <Reveal className="doordash-cta">
          <div>
            <div className="eyebrow">Can't make it to the cart?</div>
            <p className="doordash-line">Now delivering with <em>DoorDash</em>.</p>
          </div>
          <a
            href="https://doordash.com/store/42863089"
            target="_blank"
            rel="noopener noreferrer"
            className="btn-saffron"
          >
            Order on DoorDash <span className="arrow"><ExternalArrowIcon /></span>
          </a>
        </Reveal>

        <Reveal className="arancini-gallery">
          <div className="eyebrow" style={{ textAlign: 'center', marginBottom: 8 }}>Inside every arancino</div>
          <p className="gallery-hint">
            <TapIcon size={18} color="var(--terracotta)" />
            Tap any arancino to see what's inside
          </p>
          <div className="gallery-strip">
            {VISIBLE_FLAVORS.map((f) => (
              <button
                className="gallery-item"
                key={f.name}
                onClick={() => setLightbox(f)}
                aria-label={`See details for ${f.name}`}
              >
                <div className="gallery-photo">
                  <img src={`assets/${f.shape}.png`} alt={`${f.name} arancino, whole`} />
                  <span className="gallery-badge" aria-hidden="true">
                    <TapIcon size={13} color="var(--paper)" />
                  </span>
                </div>
                <div className="gallery-label">
                  <span className="display">{f.name}</span>
                  <span className="script">{f.city}</span>
                </div>
              </button>
            ))}
          </div>
        </Reveal>

        <Reveal className="specials">
          <div>
            <div className="eyebrow">Weekly specials</div>
            <h3>Ciò che <em>la Signora</em> cooks this week</h3>
            <div className="week">She changes them on purpose.</div>
            <p style={{ color: 'var(--ink-soft)', marginTop: 16, maxWidth: '38ch' }}>
              Her regulars are part of the family. The board rotates so there's
              always a reason to come back, and something new to fall in love with.
            </p>
          </div>
          <div className="specials-list">
            {specials.map((s, i) => (
              <div className="special-row" key={i}>
                <span className="it-name display">{s.it}</span>
                <span className="en-name">{s.en}</span>
                <span className="tag">{s.tag}</span>
              </div>
            ))}
          </div>
        </Reveal>

        <Reveal className="integrity-note">
          <LemonIcon />
          <p>
            <em>"If I can't find the right ingredients, I don't make the dish."</em>
            <span style={{ display: 'block', fontFamily: 'var(--font-body)', fontStyle: 'normal', fontSize: 14, letterSpacing: '.18em', textTransform: 'uppercase', color: 'var(--terracotta-d)', marginTop: 10 }}>
              — A non-negotiable house rule
            </span>
          </p>
        </Reveal>
      </div>
    </section>

    {lightbox && (
      <div className="lb-backdrop" onClick={() => setLightbox(null)}>
        <div
          className="lb-outer"
          ref={lbOuterRef}
          role="dialog"
          aria-modal="true"
          aria-labelledby="lb-title"
        >
          <button className="lb-close" onClick={() => setLightbox(null)} aria-label="Close">✕</button>
          <div className="lb-frame">
            <button className="lb-nav" onClick={(e) => { e.stopPropagation(); navigate(-1); }} aria-label="Previous">←</button>
            <div
              className="lb-panel"
              onClick={(e) => e.stopPropagation()}
              onTouchStart={onLbTouchStart}
              onTouchEnd={onLbTouchEnd}
            >
              <div className="lb-photo-wrap">
                <img src={`assets/${lightbox.name}.png`} alt={`${lightbox.name} arancino, cut open — ${lightbox.city}`} className="lb-photo" />
              </div>
              <div className="lb-body">
                <div className="lb-header">
                  <div className="lb-header-text">
                    <div className="lb-num">N° {String(VISIBLE_FLAVORS.indexOf(lightbox) + 1).padStart(2, '0')}</div>
                    <h3 className="display lb-name" id="lb-title">{lightbox.name}</h3>
                    <div className="script lb-city">{lightbox.city}</div>
                  </div>
                  <ArancinoStamp palette={lightbox.palette} />
                </div>
                <p className="lb-blurb">{lightbox.blurb}</p>
                <div className="lb-ings">
                  {lightbox.ings.map((ing, k) => <span key={k}>{ing}</span>)}
                </div>
                <div className="lb-details">
                  {lightbox.extras.map(([k, v], i) => (
                    <div className="lb-row" key={i}><span>{k}</span><b>{v}</b></div>
                  ))}
                </div>
              </div>
            </div>
            <button className="lb-nav" onClick={(e) => { e.stopPropagation(); navigate(1); }} aria-label="Next">→</button>
          </div>
        </div>
      </div>
    )}
    </React.Fragment>
  );
}

// ── Anatomy / What are arancini ──
function Anatomy() {
  return (
    <section className="anatomy" id="what">
      <div className="wrap">
        <div className="anatomy-grid">
          <Reveal>
            <div className="eyebrow">A short lesson</div>
            <div className="pron">/a·ran·CHEE·no/</div>
            <h2>So, what <em>is</em><br />an arancino?</h2>
            <p>
              A cone of slow-cooked, saffron-touched risotto stuffed with
              a savory filling, breaded by hand, and fried to the color of an
              Italian sunset. Sicilian street food at its purest — handheld,
              warm, and meant to be eaten standing up.
            </p>
            <ul>
              <li><b>01</b><span><b style={{ color: 'var(--paper)', fontFamily: 'var(--font-body)', fontStyle: 'normal' }}>The shell.</b> A thin, golden crust of toasted breadcrumbs. Audible.</span></li>
              <li><b>02</b><span><b style={{ color: 'var(--paper)', fontFamily: 'var(--font-body)', fontStyle: 'normal' }}>The rice.</b> Risotto, cooked slowly, kissed with saffron, cooled, shaped.</span></li>
              <li><b>03</b><span><b style={{ color: 'var(--paper)', fontFamily: 'var(--font-body)', fontStyle: 'normal' }}>The heart.</b> Ragù. Pistachio. Mushroom. The reason you came.</span></li>
              <li><b>04</b><span><b style={{ color: 'var(--paper)', fontFamily: 'var(--font-body)', fontStyle: 'normal' }}>The name.</b> <em>Arancino</em> or <em>arancina</em>? Sicilians have been arguing about this for centuries. Antonietta makes them in every shape — and calls them whatever she likes.</span></li>
            </ul>
          </Reveal>

          <Reveal>
            <ArancinoCrossSection />
          </Reveal>
        </div>
      </div>
    </section>
  );
}

// ── Find Us ──
const LOCATIONS = [
  {
    key: 'cathedral',
    badge: 'The Cart',
    title: 'Cathedral Park\nFood Carts',
    type: 'The original food cart',
    address: ['7431 N. Saint Louise Ave.', 'Portland, OR 97203'],
    neighborhood: 'St. Johns · six blocks from Cathedral Park',
    hours: [
      { day: 'Tue', time: '3 p.m. — 8 p.m.' },
      { day: 'Wed — Fri', time: '12 p.m. — 8 p.m.' },
      { day: 'Sat — Sun', time: 'Closed' },
    ],
    accent: 'var(--terracotta)',
    map: { scene: 'cart' },
    mapsUrl: 'https://share.google/LOsRK1SgexllICsvq'
  },
  {
    key: 'vancouver',
    badge: 'The Market',
    title: 'Vancouver\nFarmers Market',
    type: 'Weekend market booth',
    address: ['605 Esther St', 'Vancouver, WA 98660'],
    neighborhood: 'Downtown Vancouver · just over the Columbia',
    hours: [
      { day: 'Sat', time: '9 a.m. — 3 p.m.' },
      { day: 'Sun', time: '10 a.m. — 3 p.m.' },
    ],
    accent: 'var(--saffron)',
    map: { scene: 'market' },
    mapsUrl: 'https://share.google/9Iq3qnLnUDuk1FZBa'
  },
  {
    key: 'gresham',
    badge: 'The Café',
    title: 'Simply Coffee',
    type: 'Café · frozen take-home trays',
    address: ['455 NE Burnside Rd', 'Gresham, OR 97030'],
    neighborhood: 'Downtown Gresham',
    hours: [
      { day: 'Daily', time: '7 a.m. — 4 p.m.' },
    ],
    accent: 'var(--olive)',
    map: { scene: 'cafe' },
    mapsUrl: 'https://share.google/w04rvnTsM6kjX0vHr'
  },
];

function LocInfoCard({ loc, className = '' }) {
  return (
    <div
      className={`info-card ${className}`}
      style={{ '--accent': loc.accent }}
    >
      <div>
        <div className="eyebrow" style={{ color: 'var(--accent)' }}>{loc.type}</div>
        <h2>
          {loc.title.split('\n').map((line, k, arr) => (
            <React.Fragment key={k}>
              {k === arr.length - 1 ? <em>{line}</em> : <>{line}<br /></>}
            </React.Fragment>
          ))}
        </h2>
      </div>

      <div>
        <div className="info-row">
          <span className="ic" style={{ color: 'var(--accent)' }}><PinIcon /></span>
          <div>
            <b style={{ color: 'var(--accent)' }}>Address</b>
            <p>
              {loc.address.map((l, k) => <React.Fragment key={k}>{l}<br /></React.Fragment>)}
              {loc.neighborhood}
            </p>
            {loc.mapsUrl && (
              <a
                href={loc.mapsUrl}
                target="_blank"
                rel="noopener noreferrer"
                className="map-link"
                style={{ color: 'var(--accent)' }}
              >
                View on Google Maps <ExternalArrowIcon size={12} />
              </a>
            )}
          </div>
        </div>
        <div className="info-row">
          <span className="ic" style={{ color: 'var(--accent)' }}><ClockIcon /></span>
          <div>
            <b style={{ color: 'var(--accent)' }}>Hours</b>
            <p>
              {loc.hours.map((h, k) => (
                <React.Fragment key={k}>
                  <span style={{ fontWeight: 600, color: 'var(--ink)' }}>{h.day}</span>
                  {' · '}<em>{h.time}</em>
                  {k < loc.hours.length - 1 && <br />}
                </React.Fragment>
              ))}
            </p>
          </div>
        </div>
      </div>
    </div>
  );
}

function Find() {
  const [i, setI] = useState(0);
  const [cardMinH, setCardMinH] = useState(0);
  const measureRef = useRef(null);

  // Measure the tallest info-card across all locations so the grid height
  // is locked and doesn't jump between locations.
  React.useLayoutEffect(() => {
    const el = measureRef.current;
    if (!el) return;
    const measure = () => {
      const cards = el.querySelectorAll('.info-card');
      let max = 0;
      cards.forEach((c) => { max = Math.max(max, c.offsetHeight); });
      if (max > 0) setCardMinH(max);
    };
    measure();
    const ro = new ResizeObserver(measure);
    ro.observe(el);
    if (document.fonts && document.fonts.ready) document.fonts.ready.then(measure);
    return () => ro.disconnect();
  }, []);

  const loc = LOCATIONS[i];
  const next = () => setI((x) => (x + 1) % LOCATIONS.length);
  const prev = () => setI((x) => (x - 1 + LOCATIONS.length) % LOCATIONS.length);

  const minH = cardMinH || undefined;

  return (
    <section className="find" id="find">
      <div className="wrap">
        <Divider />
        <Reveal style={{ textAlign: 'center', marginBottom: 32 }}>
          <div className="eyebrow">Come hungry</div>
          <h2 className="display" style={{ fontSize: 'clamp(40px,5vw,64px)', margin: '12px 0 8px' }}>
            Find <em style={{ color: 'var(--terracotta)', fontStyle: 'italic' }}>la Signora</em>.
          </h2>
          <p style={{ color: 'var(--ink-soft)', maxWidth: '46ch', margin: '0 auto', fontSize: 17 }}>
            Three places, one Sicilian mother. The cart, the market, and a café shelf full of frozen Sunday lunch.
          </p>
        </Reveal>

        <Reveal>
          <div className="loc-tabs" role="tablist">
            {LOCATIONS.map((l, k) => (
              <button
                key={l.key}
                role="tab"
                aria-selected={k === i}
                className={`loc-tab ${k === i ? 'on' : ''}`}
                onClick={() => setI(k)}
              >
                <span className="tab-num">N° {String(k + 1).padStart(2, '0')}</span>
                <span className="tab-title">{l.badge}</span>
                <span className="tab-sub">{l.type}</span>
              </button>
            ))}
          </div>
        </Reveal>

        <Reveal>
          <div className="find-stage">
            <div
              className="find-grid"
              key={loc.key}
              data-loc={loc.key}
              style={{ minHeight: minH }}
            >
              <div
                className="map-card loc-anim"
                style={{ minHeight: minH }}
              >
                <StylizedMap data={loc.map} />
              </div>
              <LocInfoCard
                loc={loc}
                className="loc-anim"
              />
            </div>

            {/* hidden measurement layer — renders every location's info-card
                inside the same grid template so each gets the correct column
                width and we can pick the tallest one */}
            <div className="find-measure" ref={measureRef} aria-hidden="true">
              {LOCATIONS.map((l) => (
                <div className="measure-row" key={l.key}>
                  <div />
                  <LocInfoCard loc={l} className="info-card--measure" />
                </div>
              ))}
            </div>
          </div>
        </Reveal>

        <div className="loc-controls" aria-label="Location carousel controls">
          <button className="arrow-btn" onClick={prev} aria-label="Previous location">←</button>
          <div className="loc-progress" aria-hidden="true">
            {LOCATIONS.map((_, k) => (
              <span key={k} className={`tick ${k === i ? 'on' : ''} ${k < i ? 'past' : ''}`}>
                <span className="fill" style={{ width: k <= i ? '100%' : '0%' }} />
              </span>
            ))}
          </div>
          <button className="arrow-btn" onClick={next} aria-label="Next location">→</button>
        </div>

      </div>
    </section>
  );
}

// ── Reviews ──
const REVIEWS = [
  {
    quote: 'My Sicilian family and I grew up with the best Italian food in CT, and it\'s been so hard to find anything close out here. Signora Antonietta knows how to make arancini right — the ragù was exactly the real deal. 10/10, would recommend.',
    name: 'Vanessa S.',
    where: 'Local Guide · July 2026',
    via: 'Google',
    stars: 5,
  },
  {
    quote: 'La Signora feels like a little piece of home in Portland. Whenever I miss the flavors of Sicily, this is my go-to. Amazing arancini, lasagna, and cannoli — authentic, comforting, and always worth it.',
    name: 'Samuele B.',
    where: 'June 2026',
    via: 'Google',
    stars: 5,
  },
  {
    quote: 'When I was pregnant I was always craving arancini — if I\'d known there was such an authentic place making them, I\'d have gone every day! Absolutely delicious and delicate, and finally somewhere that doesn\'t overwhelm the food with spices or garlic. The cannoli are very good too: the shell is crispy, which means they\'re fresh.',
    name: 'Raffaella G.',
    where: 'Local Guide · June 2026',
    via: 'Google',
    stars: 5,
  },
  {
    quote: 'Came here on a whim and the cannoli are unreal. Perfect crunch, creamy filling, just the right sweetness. Easily some of the best I\'ve had.',
    name: 'Michael D.',
    where: 'May 2026',
    via: 'Google',
    stars: 5,
  },
  {
    quote: 'Delizioso! Un\'oasi della cucina Siciliana a Portland. We shared three types of arancini and almond granita — all delicious and well made. She even helped us practice our Italian. Highly recommend!',
    name: 'Katherine S.',
    where: 'June 2026',
    via: 'Google',
    stars: 5,
  },
  {
    quote: 'As someone who\'s half Sicilian, it\'s amazing to see arancino on the menu! So many flavors to choose from — go support them. I\'m saving the cannolo for later.',
    name: 'Jeremy F.',
    where: 'June 2026',
    via: 'Google',
    stars: 5,
  },
];

function Stars({ n = 5, color = 'var(--saffron)' }) {
  return (
    <span className="stars" aria-label={`${n} out of 5 stars`}>
      {Array.from({ length: 5 }).map((_, i) => (
        <svg key={i} width="14" height="14" viewBox="0 0 14 14" fill={i < n ? color : 'transparent'} stroke={color} strokeWidth="1" aria-hidden="true">
          <path d="M7 1 L8.7 5 L13 5.3 L9.7 8.2 L10.6 12.5 L7 10.3 L3.4 12.5 L4.3 8.2 L1 5.3 L5.3 5 Z" strokeLinejoin="round" />
        </svg>
      ))}
    </span>
  );
}

function getPerPage() {
  if (window.innerWidth <= 680) return 1;
  if (window.innerWidth <= 980) return 2;
  return 3;
}

function ReviewsSlider() {
  const [perPage, setPerPage] = useState(() => getPerPage());
  const pages = [];
  for (let i = 0; i < REVIEWS.length; i += perPage) {
    pages.push(REVIEWS.slice(i, i + perPage));
  }
  const [page, setPage] = useState(0);
  const [trackMinH, setTrackMinH] = useState(0);
  const measureRef = useRef(null);

  // Update perPage on resize and reset to page 0 to avoid out-of-bounds
  useEffect(() => {
    const onResize = () => setPerPage(getPerPage());
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);
  useEffect(() => { setPage(0); }, [perPage]);

  // Measure the tallest review-card across ALL reviews at the current
  // breakpoint so the slider height never changes between pages.
  React.useLayoutEffect(() => {
    const el = measureRef.current;
    if (!el) return;
    const measure = () => {
      const cards = [...el.querySelectorAll('.review-card')];
      let max = 0;
      for (let i = 0; i < cards.length; i += perPage) {
        const slice = cards.slice(i, i + perPage);
        const sliceMax = slice.reduce((m, c) => Math.max(m, c.offsetHeight), 0);
        max = Math.max(max, sliceMax);
      }
      if (max > 0) setTrackMinH(max);
    };
    measure();
    const ro = new ResizeObserver(measure);
    ro.observe(el);
    if (document.fonts && document.fonts.ready) document.fonts.ready.then(measure);
    return () => ro.disconnect();
  }, [perPage]);

  const prev = () => setPage((x) => (x - 1 + pages.length) % pages.length);
  const next = () => setPage((x) => (x + 1) % pages.length);

  const cur = pages[page] || pages[0];

  return (
    <div className="reviews-slider">
      <div
        className="reviews-track"
        key={page}
      >
        {cur.map((r, i) => (
          <article
            key={i}
            className="review-card"
            style={{
              animationDelay: `${i * 80}ms`,
              minHeight: trackMinH || undefined,
            }}
          >
            <div className="quote-mark" aria-hidden="true">“</div>
            <Stars n={r.stars} />
            <blockquote>{r.quote}</blockquote>
            <footer>
              <div>
                <div className="name">{r.name}</div>
                <div className="where">{r.where}</div>
              </div>
              <div className="via">via {r.via}</div>
            </footer>
          </article>
        ))}
        {/* pad to keep grid columns even when last page is short */}
        {cur.length < perPage && Array.from({ length: perPage - cur.length }).map((_, i) => (
          <div key={`pad-${i}`} className="review-pad" aria-hidden="true" />
        ))}
      </div>

      {/* hidden measurement layer — renders every review at the current grid
          width so we can pick the tallest card to lock the slider height */}
      <div className="reviews-measure" ref={measureRef} aria-hidden="true">
        {REVIEWS.map((r, i) => (
          <article key={i} className="review-card review-card--measure">
            <div className="quote-mark" aria-hidden="true">“</div>
            <Stars n={r.stars} />
            <blockquote>{r.quote}</blockquote>
            <footer>
              <div>
                <div className="name">{r.name}</div>
                <div className="where">{r.where}</div>
              </div>
              <div className="via">via {r.via}</div>
            </footer>
          </article>
        ))}
      </div>

      <div className="reviews-controls">
        <button className="arrow-btn" onClick={prev} aria-label="Previous reviews">←</button>
        <div className="loc-progress" aria-hidden="true">
          {pages.map((_, k) => (
            <span key={k} className={`tick ${k === page ? 'on' : ''} ${k < page ? 'past' : ''}`}>
              <span className="fill" style={{ width: k <= page ? '100%' : '0%' }} />
            </span>
          ))}
        </div>
        <button className="arrow-btn" onClick={next} aria-label="Next reviews">→</button>
      </div>
    </div>
  );
}

// Real aggregate from La Signora's Google Business listing (not the
// featured subset below) — keep in sync with the live Google rating.
const RATING = '4.6';
const REVIEW_COUNT = '20+';

function Reviews() {
  return (
    <section className="reviews" id="reviews">
      <div className="wrap">
        <Divider />
        <Reveal className="reviews-head">
          <div>
            <div className="eyebrow">From her regulars</div>
            <h2 className="display">
              They keep<br /><em>coming back.</em>
            </h2>
          </div>
          <div className="reviews-stat">
            <div className="big"><em>{RATING}</em><span>/ 5</span></div>
            <div>
              <Stars n={5} />
              <p>From {REVIEW_COUNT} reviews on Google.</p>
            </div>
          </div>
        </Reveal>

        <Reveal>
          <ReviewsSlider />
        </Reveal>
      </div>
    </section>
  );
}
const OFFERINGS = [
  {
    n: '01',
    badge: 'Private events',
    title: 'Birthdays,\nanniversaries,\nsupper clubs.',
    blurb: 'A full Sicilian table for 6 to 40, brought to your kitchen or your backyard. We handle the cooking and the cleanup — you pour the wine.',
    bullets: ['6 — 40 guests', 'Cooked on-site or pre-plated', 'Full setup & cleanup'],
    palette: ['#B5482B', '#8C341E', '#E0A638'],
  },
  {
    n: '02',
    badge: 'Corporate events',
    title: 'Arancino stations,\nteam lunches,\nholiday parties.',
    blurb: 'An arancino station at your launch, a lasagna lunch for the engineering team, hand-fried cannoli for the holiday party. We bring the cart, the smile, and the food.',
    bullets: ['Office lunches & launches', 'Cart pop-ups on-site', 'Cannoli & espresso stations'],
    palette: ['#E0A638', '#B07A2D', '#5C6B33'],
  },
  {
    n: '03',
    badge: 'Wholesale supply',
    title: 'Frozen trays for\ncafés, delis,\nbottle shops.',
    blurb: 'Frozen arancini and lasagna trays for cafés, delis, and bottle shops across the Portland-Vancouver metro. Small minimums, weekly drops.',
    bullets: ['Portland-Vancouver metro', 'Small minimums', 'Weekly drops'],
    palette: ['#5C6B33', '#3F4A21', '#E6D2A9'],
  },
];

function Catering() {
  return (
    <section className="catering" id="catering">
      <div className="catering-tile" aria-hidden="true"></div>
      <div className="wrap">
        <Divider style={{ color: 'var(--saffron)', '--line': 'rgba(240,220,180,0.22)' }} />
        <Reveal className="catering-head">
          <div>
            <div className="eyebrow saffron">Catering & events</div>
            <h2 className="display">
              Have her cook<br />for <em>your table.</em>
            </h2>
          </div>
          <p>
            Out of arancini? Let la Signora come to you. Private dinners,
            corporate pop-ups, wholesale trays — same hands, same recipes —
            one kitchen (or one entire conference floor) full of people
            you love.
          </p>
        </Reveal>

        <Reveal>
          <div className="catering-grid">
            {OFFERINGS.map((o, i) => (
              <article key={i} className="cat-card">
                <header className="cat-card-head">
                  <ArancinoStamp palette={o.palette} />
                  <div className="num">N° {o.n}</div>
                </header>
                <div className="tag" style={{ color: o.palette[0] }}>{o.badge}</div>
                <h3 className="display">
                  {o.title.split('\n').map((l, k, arr) => (
                    <React.Fragment key={k}>
                      {l}{k < arr.length - 1 && <br />}
                    </React.Fragment>
                  ))}
                </h3>
                <p>{o.blurb}</p>
                <ul>
                  {o.bullets.map((b, k) => <li key={k}><span className="bul" style={{ background: o.palette[0] }}></span>{b}</li>)}
                </ul>
                <a className="cat-cta" href="contact.html" style={{ color: o.palette[0], borderColor: o.palette[0] }}>
                  Inquire <span className="arrow">→</span>
                </a>
              </article>
            ))}
          </div>
        </Reveal>

        <Reveal className="catering-foot">
          <div>
            <div className="script">Tell us about your event.</div>
            <p>Two weeks' notice helps. Two days' notice has worked before.</p>
          </div>
          <a className="btn btn-primary" href="contact.html">
            Get in touch <span className="arrow">→</span>
          </a>
        </Reveal>
      </div>
    </section>
  );
}

// ── Contact ──
const TOPICS = [
  { v: 'catering', label: 'Catering or events' },
  { v: 'wholesale', label: 'Wholesale supply' },
  { v: 'restaurant', label: 'Restaurant updates' },
  { v: 'media', label: 'Press / media' },
  { v: 'hello', label: 'Just saying ciao' },
];

const CHANNELS = [
  { key: 'instagram', label: 'Instagram', handle: '@la_signora_wa', href: 'https://www.instagram.com/la_signora_wa/', Icon: IgIcon },
  { key: 'tiktok', label: 'TikTok', handle: '@la_signora_wa', href: 'https://www.tiktok.com/@la_signora_wa', Icon: TikTokIcon },
  { key: 'facebook', label: 'Facebook', handle: 'La Signora', href: 'https://www.facebook.com/LaSignoraItalianGoodFood/', Icon: FbIcon },
  { key: 'email', label: 'Email', handle: 'info@lasignorafood.com', href: 'mailto:info@lasignorafood.com', Icon: MailIcon },
];

// reCAPTCHA v2 site key — set in each page's <head> as `window.RECAPTCHA_SITE_KEY`.
// When empty the widget is skipped and the form falls back to the server-side
// honeypot + rate limit (see contact-handler.php).
const RECAPTCHA_SITE_KEY =
  (typeof window !== 'undefined' && window.RECAPTCHA_SITE_KEY) || '';

function Contact() {
  const [form, setForm] = useState({ topic: 'catering', name: '', email: '', message: '', company: '' });
  const [errors, setErrors] = useState({});
  const [sent, setSent] = useState(false);
  const [sending, setSending] = useState(false);
  const [submitError, setSubmitError] = useState('');
  const recaptchaRef = useRef(null);
  const widgetIdRef = useRef(null);

  const set = (k, v) => {
    setForm((f) => ({ ...f, [k]: v }));
    setErrors((e) => ({ ...e, [k]: undefined }));
  };

  // Render the reCAPTCHA widget once the API is ready. The container only
  // exists while the form is shown, so re-render after a reset (sent → false).
  useEffect(() => {
    if (sent) { widgetIdRef.current = null; return; }
    if (!RECAPTCHA_SITE_KEY) return;
    let cancelled = false;
    let timer;
    const render = () => {
      if (cancelled) return;
      const el = recaptchaRef.current;
      const gre = window.grecaptcha;
      if (gre && gre.render && el && widgetIdRef.current === null && el.childElementCount === 0) {
        widgetIdRef.current = gre.render(el, { sitekey: RECAPTCHA_SITE_KEY });
        return;
      }
      timer = setTimeout(render, 300);
    };
    render();
    return () => { cancelled = true; clearTimeout(timer); };
  }, [sent]);

  const resetCaptcha = () => {
    if (window.grecaptcha && widgetIdRef.current !== null) {
      window.grecaptcha.reset(widgetIdRef.current);
    }
  };

  const submit = async (e) => {
    e.preventDefault();
    const errs = {};
    if (!form.name.trim()) errs.name = 'Please share your name.';
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email.trim())) errs.email = 'A real email, per favore.';
    if (!form.message.trim() || form.message.trim().length < 10) errs.message = 'Tell us a little more.';
    setErrors(errs);
    if (Object.keys(errs).length > 0) return;

    // reCAPTCHA — require a token before we bother the server.
    let captchaToken = '';
    if (RECAPTCHA_SITE_KEY) {
      captchaToken = (window.grecaptcha && widgetIdRef.current !== null)
        ? window.grecaptcha.getResponse(widgetIdRef.current)
        : '';
      if (!captchaToken) {
        setSubmitError('Please confirm you’re human with the CAPTCHA below.');
        return;
      }
    }

    setSending(true);
    setSubmitError('');
    try {
      const payload = new URLSearchParams(form);
      if (captchaToken) payload.set('g-recaptcha-response', captchaToken);
      const res = await fetch('contact-handler.php', {
        method: 'POST',
        body: payload,
      });
      const data = await res.json();
      if (!res.ok || !data.success) throw new Error(data.error || 'send failed');
      setSent(true);
    } catch {
      setSubmitError('Something went wrong sending your message. Please try again, or email info@lasignorafood.com directly.');
      resetCaptcha();
    } finally {
      setSending(false);
    }
  };

  const reset = () => {
    setForm({ topic: 'catering', name: '', email: '', message: '', company: '' });
    setErrors({});
    setSent(false);
    setSubmitError('');
  };

  const topicLabel = TOPICS.find((t) => t.v === form.topic)?.label || '';

  return (
    <section className="contact" id="contact">
      <div className="wrap">
        <div className="contact-grid">
          <Reveal className="contact-aside">
            <div className="eyebrow" style={{ color: 'var(--olive-d)' }}>Write to la Signora</div>
            <h2 className="display">
              Tell us<br />what brings<br /><em>you here.</em>
            </h2>
            <p>
              Catering, wholesale, a question about Sunday's lasagna — or
              maybe you just want to say <em>ciao</em>. Antonietta reads
              everything herself. (Yes, even the long ones.)
            </p>

            <div className="contact-note">
              <b>Reply within</b>
              <p>1 — 2 days. Slower in summer market season.</p>
            </div>

            <div className="contact-channels">
              <div className="channels-label">Find her here</div>
              {CHANNELS.map(({ key, label, handle, href, Icon }) => {
                const external = key !== 'email';
                return (
                  <a
                    key={key}
                    href={href}
                    className="channel"
                    {...(external ? { target: '_blank', rel: 'noopener noreferrer' } : {})}
                  >
                    <span className="channel-ic"><Icon size={20} color="currentColor" /></span>
                    <span className="channel-text">
                      <span className="channel-name">{label}</span>
                      <span className="channel-handle">{handle}</span>
                    </span>
                    {external && <span className="channel-arrow"><ExternalArrowIcon size={12} /></span>}
                  </a>
                );
              })}
            </div>
          </Reveal>

          <Reveal className={`contact-card ${sent ? 'is-sent' : ''}`}>
            {!sent ? (
              <form onSubmit={submit} noValidate>
                <div className="field">
                  <label htmlFor="topic">What's this about?</label>
                  <div className="seg-wrap">
                    {TOPICS.map((t) => (
                      <button
                        type="button"
                        key={t.v}
                        className={`seg ${form.topic === t.v ? 'on' : ''}`}
                        onClick={() => set('topic', t.v)}
                      >{t.label}</button>
                    ))}
                  </div>
                </div>

                <div className="field-row">
                  <div className="field">
                    <label htmlFor="name">Your name</label>
                    <input
                      id="name"
                      type="text"
                      value={form.name}
                      onChange={(e) => set('name', e.target.value)}
                      placeholder="Antonietta, for example"
                      className={errors.name ? 'invalid' : ''}
                      autoComplete="name"
                    />
                    {errors.name && <span className="err">{errors.name}</span>}
                  </div>
                  <div className="field">
                    <label htmlFor="email">Email</label>
                    <input
                      id="email"
                      type="email"
                      value={form.email}
                      onChange={(e) => set('email', e.target.value)}
                      placeholder="your@email.com"
                      className={errors.email ? 'invalid' : ''}
                      autoComplete="email"
                    />
                    {errors.email && <span className="err">{errors.email}</span>}
                  </div>
                </div>

                <div className="field">
                  <label htmlFor="message">
                    Your message
                    <span className="hint">
                      {form.topic === 'catering' && 'Date, headcount, location.'}
                      {form.topic === 'wholesale' && 'Your café/shop, location, volume.'}
                      {form.topic === 'restaurant' && 'Just leave a note — we\'ll keep you posted.'}
                      {form.topic === 'media' && 'Outlet, deadline, the gist.'}
                      {form.topic === 'hello' && 'Tell her how the food tasted.'}
                    </span>
                  </label>
                  <textarea
                    id="message"
                    rows="5"
                    value={form.message}
                    onChange={(e) => set('message', e.target.value)}
                    placeholder="A few lines is plenty…"
                    className={errors.message ? 'invalid' : ''}
                  />
                  {errors.message && <span className="err">{errors.message}</span>}
                </div>

                {/* Honeypot — hidden from humans, catches auto-fill bots.
                    Hidden via inline style too, so a stale/cached styles.css
                    can never expose it. */}
                <div
                  className="hp-field"
                  aria-hidden="true"
                  style={{ position: 'absolute', left: '-9999px', width: 1, height: 1, overflow: 'hidden' }}
                >
                  <label htmlFor="company">Company</label>
                  <input
                    id="company"
                    type="text"
                    name="company"
                    tabIndex={-1}
                    autoComplete="off"
                    value={form.company}
                    onChange={(e) => set('company', e.target.value)}
                  />
                </div>

                {RECAPTCHA_SITE_KEY && (
                  <div className="captcha-field">
                    <div ref={recaptchaRef} className="g-recaptcha-holder" />
                    <p className="recaptcha-notice">
                      This site is protected by reCAPTCHA and the Google{' '}
                      <a href="https://policies.google.com/privacy" target="_blank" rel="noopener noreferrer">Privacy Policy</a>{' '}
                      and{' '}
                      <a href="https://policies.google.com/terms" target="_blank" rel="noopener noreferrer">Terms of Service</a>{' '}
                      apply.
                    </p>
                  </div>
                )}

                <div className="form-foot">
                  <p>By writing, you agree we may reply. No lists, no spam.</p>
                  {submitError && <span className="err">{submitError}</span>}
                  <button type="submit" className="btn btn-primary" disabled={sending}>
                    {sending ? 'Sending…' : <>Send to la Signora <span className="arrow">→</span></>}
                  </button>
                </div>
              </form>
            ) : (
              <div className="thanks">
                <div className="thanks-mark">
                  <svg viewBox="0 0 80 80" width="80" height="80" fill="none" aria-hidden="true">
                    <circle cx="40" cy="40" r="36" stroke="var(--terracotta)" strokeWidth="1.5" />
                    <path d="M24 41 L36 53 L58 28" stroke="var(--terracotta)" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" />
                  </svg>
                </div>
                <div className="script" style={{ color: 'var(--terracotta)', fontSize: 56, lineHeight: 1, margin: '8px 0 4px' }}>Grazie, {form.name.split(' ')[0]}!</div>
                <p>
                  Your note about <em>{topicLabel.toLowerCase()}</em> is on its way.
                  Antonietta reads each one. You'll hear back in 1 — 2 days.
                </p>
                <button type="button" className="btn btn-ghost" onClick={reset}>
                  Send another
                </button>
              </div>
            )}
          </Reveal>
        </div>
      </div>
    </section>
  );
}

// ── Privacy Policy ──
function PrivacyPolicy() {
  return (
    <section className="privacy">
      <div className="wrap">
        <Reveal>
          <h1 style={{ fontSize: 'clamp(28px, 4.4vw, 40px)', marginBottom: 8 }}>Privacy Policy</h1>
          <p style={{ fontSize: '16px', color: 'var(--ink-soft)', marginBottom: 28 }}>Last updated July 2026</p>
        </Reveal>

        <Reveal>
          <h2>Overview</h2>
          <p>La Signora respects your privacy. This policy explains what information we collect, why we collect it, and how we protect it.</p>
        </Reveal>

        <Reveal>
          <h2>Information We Collect</h2>
          <h3>Contact form</h3>
          <p>When you submit our contact form, we collect your name, email, topic, and message. This is used only to respond to your inquiry.</p>
          <h3>IP address</h3>
          <p>We hash your IP address temporarily to prevent form spam. This hash is deleted after one hour.</p>
          <h3>Google Fonts</h3>
          <p>We load fonts from Google Fonts. Google may collect your browser type and IP address. See their <a href="https://policies.google.com/privacy">privacy policy</a>.</p>
          <h3>reCAPTCHA</h3>
          <p>Our contact form uses Google reCAPTCHA v2 to verify you are human. This site is protected by reCAPTCHA and the <a href="https://policies.google.com/privacy">Google Privacy Policy</a> and <a href="https://policies.google.com/terms">Terms of Service</a> apply.</p>
        </Reveal>

        <Reveal>
          <h2>How We Use Your Information</h2>
          <ul style={{ marginLeft: '20px', marginBottom: 16 }}>
            <li>To respond to your message</li>
            <li>To prevent spam and abuse</li>
            <li>To deliver a working website</li>
          </ul>
        </Reveal>

        <Reveal>
          <h2>Data Retention</h2>
          <ul style={{ marginLeft: '20px', marginBottom: 16 }}>
            <li>Contact messages: retained indefinitely</li>
            <li>Rate-limit hashes: deleted after 1 hour</li>
            <li>Google data: retained per their policies</li>
          </ul>
        </Reveal>

        <Reveal>
          <h2>Third Parties</h2>
          <p>We do not sell your information. Google Fonts and reCAPTCHA may collect data per their policies. We do not use analytics or advertising cookies.</p>
        </Reveal>

        <Reveal>
          <h2>Security</h2>
          <p>We use HTTPS encryption, validate all submissions, and implement rate-limiting and bot detection.</p>
        </Reveal>

        <Reveal>
          <h2>Your Rights</h2>
          <p>You can request access, correction, or deletion of your data. Contact us at <a href="mailto:info@lasignorafood.com">info@lasignorafood.com</a>.</p>
        </Reveal>

        <Reveal>
          <h2>Changes</h2>
          <p>We may update this policy. Your use of the site means you accept any changes.</p>
        </Reveal>

        <Reveal>
          <h2>Contact</h2>
          <p><strong>La Signora</strong><br /><a href="mailto:info@lasignorafood.com">info@lasignorafood.com</a></p>
        </Reveal>
      </div>
    </section>
  );
}

// ── Accessibility Statement ──
function AccessibilityStatement() {
  return (
    <section className="privacy">
      <div className="wrap">
        <Reveal>
          <h1 style={{ fontSize: 'clamp(28px, 4.4vw, 40px)', marginBottom: 8 }}>Accessibility Statement</h1>
          <p style={{ fontSize: '16px', color: 'var(--ink-soft)', marginBottom: 28 }}>Last updated July 2026</p>
        </Reveal>

        <Reveal>
          <h2>Our Commitment</h2>
          <p>La Signora is committed to making this website usable by everyone, including people who use assistive technology such as screen readers, switch devices, or keyboard-only navigation. We aim to meet the Web Content Accessibility Guidelines (WCAG) 2.1 at Level AA.</p>
        </Reveal>

        <Reveal>
          <h2>What We've Done</h2>
          <ul style={{ marginLeft: '20px', marginBottom: 16 }}>
            <li>Keyboard navigation for the menu, mobile navigation drawer, and photo lightbox, including a "skip to content" link and visible focus states</li>
            <li>Descriptive alt text on meaningful images, and decorative icons hidden from screen readers</li>
            <li>Proper heading structure and landmarks (navigation, main content, footer)</li>
            <li>Color combinations checked against WCAG contrast requirements</li>
            <li>Respect for the "reduce motion" setting on animations</li>
          </ul>
        </Reveal>

        <Reveal>
          <h2>Ongoing Work</h2>
          <p>Accessibility is an ongoing effort, not a one-time fix. We review the site as we add new features and welcome feedback on anything that's hard to use.</p>
        </Reveal>

        <Reveal>
          <h2>Report a Problem</h2>
          <p>If you encounter an accessibility barrier on this website, please let us know — include the page and what happened, and we'll work to fix it. Contact us at <a href="mailto:info@lasignorafood.com">info@lasignorafood.com</a>.</p>
        </Reveal>
      </div>
    </section>
  );
}

// ── Footer ──
function Footer() {
  const year = new Date().getFullYear();
  return (
    <footer className="foot">
      <div className="wrap">
        <div className="foot-grid">
          <div>
            <h4 className="display">La <em>Signora</em></h4>
            <p style={{ maxWidth: '32ch' }}>
              Family recipes from Sicily, made with love in Portland.
            </p>
            <p style={{ fontFamily: 'var(--font-script)', fontSize: 22, color: 'var(--saffron)', marginTop: 12 }}>
              Mangia. Sorridi. Torna presto.
            </p>
          </div>
          <div>
            <b>Visit</b>
            <p>7431 N. Saint Louise Ave.<br />Portland, OR 97203</p>
            <p>Wed — Sun</p>
          </div>
          <div>
            <b>Order &amp; Follow</b>
            <a href="https://doordash.com/store/42863089" target="_blank" rel="noopener noreferrer">Order on DoorDash <span className="arrow"><ExternalArrowIcon size={12} /></span></a>
            <a href="https://www.instagram.com/la_signora_wa/" target="_blank" rel="noopener noreferrer">Instagram <span className="arrow"><ExternalArrowIcon size={12} /></span></a>
            <a href="https://www.facebook.com/LaSignoraItalianGoodFood/" target="_blank" rel="noopener noreferrer">Facebook <span className="arrow"><ExternalArrowIcon size={12} /></span></a>
            <a href="https://www.tiktok.com/@la_signora_wa" target="_blank" rel="noopener noreferrer">TikTok <span className="arrow"><ExternalArrowIcon size={12} /></span></a>
            <a href="mailto:info@lasignorafood.com">info@lasignorafood.com</a>
          </div>
          <div>
            <b>Legal</b>
            <a href="privacy.html">Privacy</a>
            <a href="accessibility.html">Accessibility</a>
          </div>
        </div>
        <div className="foot-bottom">
          <span>© {year} La Signora. Made in Portland with Sicilian hands.</span>
          <span style={{ fontFamily: 'var(--font-script)', fontSize: 18, color: 'var(--saffron)' }}>grazie ✦</span>
        </div>
      </div>
    </footer>
  );
}

Object.assign(window, { Reveal, Nav, Hero, Strip, Story, Menu, Anatomy, Reviews, Find, Catering, Contact, PrivacyPolicy, AccessibilityStatement, Footer });
