// Pocket CEO — Local SMB LP components
const { useState, useEffect, useRef } = React;

const scrollToId = (id, offset = 80) => {
  const el = document.getElementById(id);
  if (el) window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - offset, behavior: 'smooth' });
};

const Eyebrow = ({ children, dark, center }) => (
  <span className="pc-eyebrow" style={{ ...(center ? { justifyContent: 'center' } : {}), ...(dark ? { color: 'rgba(250,250,247,0.85)' } : {}) }}>{children}</span>
);

const Nav = ({ onCtaClick }) => {
  const [open, setOpen] = useState(false);
  const [mobileOpen, setMobileOpen] = useState(false);

  // Lock body scroll + Esc-to-close while mobile menu is open
  useEffect(() => {
    if (!mobileOpen) return;
    const onKey = (e) => { if (e.key === 'Escape') setMobileOpen(false); };
    document.addEventListener('keydown', onKey);
    const prev = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => {
      document.removeEventListener('keydown', onKey);
      document.body.style.overflow = prev;
    };
  }, [mobileOpen]);

  const closeMobile = () => setMobileOpen(false);

  return (
    <nav className={`pc-nav ${mobileOpen ? 'is-mobile-open' : ''}`} id="nav">
      <div className="pc-container pc-nav-inner">
        <div className="pc-nav-left">
          <a className="pc-brand" href="https://gopocketceo.com" aria-label="Pocket CEO — main site">
            <img className="pc-brand-lockup" src="assets/pocket-ceo-logo.webp" alt="Pocket CEO" width="207" height="40" />
            <span className="pc-brand-subtitle"><em>Local Businesses</em></span>
          </a>
          <div className="pc-nav-center">
            <a href="https://gopocketceo.com/#why">Stack</a>
            <div className={`pc-nav-dd ${open ? 'is-open' : ''}`} onMouseLeave={() => setOpen(false)}>
              <button type="button" className="pc-nav-dd-toggle" onClick={() => setOpen(v => !v)} aria-expanded={open}>
                Industries <span className="pc-nav-dd-arrow">▾</span>
              </button>
              <div className="pc-nav-dd-menu" role="menu">
                <a href="/" className="is-current" role="menuitem">
                  <strong>Local Business</strong>
                  <span>HK service-locals · this page</span>
                </a>
                <a href="https://gopocketceo.com/insurance/" role="menuitem">
                  <strong>Insurance Brokers</strong>
                  <span>HK IA-licensed firms</span>
                </a>
                <a href="https://gopocketceo.com/#industries" role="menuitem" className="pc-nav-dd-all">
                  See all industries →
                </a>
              </div>
            </div>
            <a href="https://gopocketceo.com/#process">Process</a>
            <a href="https://gopocketceo.com/#founders">Founders</a>
          </div>
        </div>
        <div className="pc-nav-right">
          <a className="pc-btn pc-btn-primary pc-btn-sm pc-nav-cta-desktop" href="#booking" onClick={onCtaClick}>Become a Pocket CEO →</a>
          <button
            type="button"
            className="pc-nav-burger"
            aria-label={mobileOpen ? 'Close menu' : 'Open menu'}
            aria-expanded={mobileOpen}
            aria-controls="pc-nav-mobile-panel"
            onClick={() => setMobileOpen(v => !v)}
          >
            <span aria-hidden="true"></span>
            <span aria-hidden="true"></span>
            <span aria-hidden="true"></span>
          </button>
        </div>
      </div>
      <div className="pc-nav-mobile-backdrop" onClick={closeMobile} aria-hidden="true"></div>
      <aside
        id="pc-nav-mobile-panel"
        className="pc-nav-mobile-panel"
        role="dialog"
        aria-modal="true"
        aria-label="Site menu"
        aria-hidden={!mobileOpen}
      >
        <nav className="pc-nav-mobile-links" aria-label="Mobile navigation">
          <a href="https://gopocketceo.com/#why" onClick={closeMobile}>Stack</a>
          <a href="/" className="is-current" onClick={closeMobile}>Local Business</a>
          <a href="https://gopocketceo.com/insurance/" onClick={closeMobile}>Insurance Brokers</a>
          <a href="https://gopocketceo.com/#industries" onClick={closeMobile}>All industries</a>
          <a href="https://gopocketceo.com/#process" onClick={closeMobile}>Process</a>
          <a href="https://gopocketceo.com/#founders" onClick={closeMobile}>Founders</a>
        </nav>
        <a
          className="pc-btn pc-btn-primary pc-nav-mobile-cta"
          href="#booking"
          onClick={(e) => { closeMobile(); if (onCtaClick) onCtaClick(e); }}
        >
          Become a Pocket CEO →
        </a>
      </aside>
    </nav>
  );
};

/* ---------- §01 HERO · video-first centered (mirrors agency-landing-page) ---------- */
const Hero = () => {
  const videoRef = React.useRef(null);
  const [muted, setMuted] = React.useState(true);

  const onToggleSound = () => {
    const v = videoRef.current;
    if (!v) return;
    const next = !v.muted;
    v.muted = next;
    setMuted(next);
    if (!next) {
      const p = v.play();
      if (p && typeof p.catch === 'function') p.catch(() => {});
    }
  };

  return (
  <header className="pc-hero-centered" id="hero">
    <div className="pc-hero-inner">
      <span className="pc-mono-eyebrow">HONG KONG · LOCAL SERVICE BUSINESSES</span>
      <h1 className="pc-hero-h1">
        <span className="pc-hero-line">Run the business.</span>
        <span className="pc-hero-line">Not the operation.</span>
      </h1>
      <p className="pc-hero-lede">
        For HK service businesses. We build the digital infrastructure that captures every customer and takes the niche-specific work off your desk.
      </p>

      <div className="pc-hero-video-perspective">
        <figure className="pc-hero-video-frame">
          <video
            ref={videoRef}
            id="hero-video"
            src="assets/hero-video.mp4?v=2"
            poster="assets/hero-poster.webp"
            autoPlay
            muted
            loop
            playsInline
            preload="metadata"
          ></video>
          <button
            type="button"
            className="pc-hero-video-controls"
            id="hero-video-sound"
            aria-label={muted ? 'Unmute video' : 'Mute video'}
            aria-pressed={!muted}
            data-muted={muted ? 'true' : 'false'}
            onClick={onToggleSound}
          >
            <span className="pc-hero-video-sound-icon" aria-hidden="true">
              <svg className="icon-muted" width="12" height="12" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" style={{ display: muted ? 'block' : 'none' }}><path d="M7.5 3L4 6H2v4h2l3.5 3V3z" fill="currentColor" /><path d="M11 6l3 3M14 6l-3 3" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" /></svg>
              <svg className="icon-unmuted" width="12" height="12" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" style={{ display: muted ? 'none' : 'block' }}><path d="M7.5 3L4 6H2v4h2l3.5 3V3z" fill="currentColor" /><path d="M10.5 5.5a3 3 0 010 5M12.5 4a5 5 0 010 8" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" fill="none" /></svg>
            </span>
            <span className="pc-hero-video-sound-label">{muted ? 'UNMUTE' : 'MUTE'}</span>
          </button>
        </figure>
      </div>

      <div className="pc-hero-cta-row">
        <a className="pc-btn pc-btn-primary" href="#booking" onClick={(e) => { e.preventDefault(); scrollToId('booking'); }}>Become a Pocket CEO →</a>
        <a className="pc-btn pc-btn-text" href="#solution" onClick={(e) => { e.preventDefault(); scrollToId('solution'); }}>See the stack →</a>
      </div>

      <div className="pc-hero-metrics">
        <div className="pc-hero-metric">
          <span className="pc-hero-metric-num">4 to 6 WEEKS</span>
          <span className="pc-hero-metric-label">MVP live</span>
        </div>
        <div className="pc-hero-metric">
          <span className="pc-hero-metric-num">24-HR</span>
          <span className="pc-hero-metric-label">Audit Loom turnaround</span>
        </div>
        <div className="pc-hero-metric">
          <span className="pc-hero-metric-num">YOU OWN IT</span>
          <span className="pc-hero-metric-label">We run it</span>
        </div>
      </div>
    </div>
  </header>
  );
};

/* ---------- §02 PAIN ---------- */
const PainSection = () => (
  <section className="pc-section" id="pain">
    <div className="pc-container">
      <div className="pc-section-head">
        <Eyebrow center>§ 02 · Where the revenue goes</Eyebrow>
        <h2 className="pc-display-lg">Most service businesses lose 20% of revenue.</h2>
        <p className="pc-lede">Not because the work isn't good. Because the digital infrastructure around it doesn't exist. Three places where it shows up every week.</p>
      </div>
      <div className="pc-pain-grid" data-snap-rail="pain">
        {/* CARD 1 — calls */}
        <div className="pc-pain-card pain-tone-orange">
          <Eyebrow>Calls during the rush</Eyebrow>
          <h3>Every missed call walks to the next shop.</h3>
          <p>You can't pick up. Five minutes later they search Central, Sheung Wan, Wan Chai and book somewhere else. That's $300+ in services walking out, multiple times a week.</p>
          <div className="pc-pain-visual pv-calls-v2">
            <div className="rush-clock">
              <span className="rush-time">14:02</span>
              <span className="rush-meta">PEAK · LUNCH</span>
            </div>
            <div className="rush-rings">
              <div className="rush-ring r1"></div>
              <div className="rush-ring r2"></div>
              <div className="rush-ring r3"></div>
              <div className="rush-phone">☏</div>
            </div>
            <div className="rush-walker">
              <span className="rush-walker-dot"></span>
              <span className="rush-walker-line"></span>
              <span className="rush-walker-target">NEXT SHOP</span>
            </div>
            <div className="rush-loss">−HK$300</div>
          </div>
        </div>

        {/* CARD 2 — WhatsApp */}
        <div className="pc-pain-card pain-tone-navy">
          <Eyebrow>WhatsApp chaos</Eyebrow>
          <h3>New customers don't wait three hours.</h3>
          <p>40 WhatsApp threads, double-bookings on Saturday, the customer who texts at 11pm and gets ignored. They book elsewhere and don't come back.</p>
          <div className="pc-pain-visual pv-wa-v2">
            <div className="wa-thread t1">
              <span className="wa-name">Karen L.</span>
              <span className="wa-snippet">Saturday 4pm available?</span>
              <span className="wa-time">14:02</span>
            </div>
            <div className="wa-thread t2">
              <span className="wa-name">+852 9012</span>
              <span className="wa-snippet">price for cut + colour?</span>
              <span className="wa-time">14:08</span>
            </div>
            <div className="wa-thread t3 late">
              <span className="wa-name">Unread · 23</span>
              <span className="wa-snippet">…</span>
              <span className="wa-time wa-time-late">3h</span>
            </div>
            <div className="wa-thread t4 late">
              <span className="wa-name">Sat double-book</span>
              <span className="wa-snippet">2 customers · 16:00</span>
              <span className="wa-time wa-time-late">!</span>
            </div>
            <div className="wa-thread t5">
              <span className="wa-name">+852 5587</span>
              <span className="wa-snippet">still open?</span>
              <span className="wa-time">23:14</span>
            </div>
          </div>
        </div>

        {/* CARD 3 — reviews */}
        <div className="pc-pain-card pain-tone-fade">
          <Eyebrow>Reviews + reactivation</Eyebrow>
          <h3>Hundreds of regulars, zero online presence.</h3>
          <p>Last new Google review: 2021. Dormant regulars never asked back. That's the difference between someone googling you and walking past.</p>
          <div className="pc-pain-visual pv-rev-gap">
            {/* Offline — hundreds of happy regulars (dense dot pool) */}
            <div className="rev-gap-side rev-gap-offline">
              <div className="rev-gap-grid" aria-hidden="true">
                {Array.from({ length: 32 }).map((_, i) => (
                  <span
                    key={i}
                    className="rev-gap-dot"
                    style={{ animationDelay: `${(i % 8) * 0.04 + Math.floor(i / 8) * 0.06}s` }}
                  />
                ))}
              </div>
              <div className="rev-gap-caption">
                <strong>247</strong> happy · 4.9 ★
              </div>
            </div>

            {/* Divider — the trust gap */}
            <div className="rev-gap-divider" aria-hidden="true">
              <span className="rev-gap-divider-label">gap</span>
            </div>

            {/* Online — zero reviews captured */}
            <div className="rev-gap-side rev-gap-online">
              <div className="rev-gap-stars" aria-hidden="true">
                <span>★</span><span>★</span><span>★</span><span>★</span><span>★</span>
              </div>
              <div className="rev-gap-zero">0</div>
              <div className="rev-gap-caption rev-gap-caption-orange">new · since 2021</div>
            </div>

            {/* Ghost reviews — potential reviews escaping unsolicited */}
            <span className="rev-gap-ghost rev-gap-ghost-1" aria-hidden="true">+1 ★</span>
            <span className="rev-gap-ghost rev-gap-ghost-2" aria-hidden="true">+1 ★</span>
            <span className="rev-gap-ghost rev-gap-ghost-3" aria-hidden="true">+1 ★</span>
          </div>
        </div>
      </div>
      <div className="pc-snap-dots" data-snap-dots="pain" role="tablist" aria-label="Pain section navigation">
        <button type="button" role="tab" aria-selected="true" className="is-active" aria-label="Card 1 of 3: Calls during the rush"></button>
        <button type="button" role="tab" aria-selected="false" aria-label="Card 2 of 3: WhatsApp chaos"></button>
        <button type="button" role="tab" aria-selected="false" aria-label="Card 3 of 3: Reviews and reactivation"></button>
      </div>
    </div>
  </section>
);

/* ---------- §03 SOLUTION ---------- */
const LAYER_DATA = {
  1: {
    eyebrow: '§ 03 · Layer 1 · Digital Infrastructure',
    title: 'The foundation every service business needs.',
    sub: 'Universal. Built once, works the same regardless of niche. Captures every customer · website, instant reply, reactivation, reviews.',
    cards: [
      {
        id: 'site',
        label: '1.1 · Smart Website',
        h: 'A site that ranks, loads fast, and converts.',
        features: [
          {
            iconKey: 'seo',
            title: 'Found on Google first',
            desc: 'When locals search your service nearby, you show up. Not the shop down the street.',
          },
          {
            iconKey: 'lighthouse',
            title: 'Looks professional everywhere',
            desc: 'Crisp on phones, tablets, desktops. Nothing breaks. Visitors trust it the second it opens.',
          },
          {
            iconKey: 'speed',
            title: 'Opens in a second',
            desc: 'Most local sites take four. Yours loads before they get bored — fewer drop-offs, more bookings.',
          },
          {
            iconKey: 'bilingual',
            title: 'Speaks Cantonese, properly',
            desc: 'A native speaker writes the 繁中 — not Google Translate. Locals notice. They book.',
          },
        ],
        kpi: '4-second load → 1.2 seconds',
        cta: 'Audit my website →',
        visual: 'site',
        imageAlt: 'Smart website mockup · phone with bilingual booking flow + Google Business listing',
      },
      {
        id: 'callback',
        label: '1.2 · Missed-Call Text-Back',
        h: 'Every call captured, even off-hours.',
        features: [
          {
            iconKey: 'clock',
            title: 'Reply before they call the next shop',
            desc: 'Miss a call at lunch rush? Within 30 seconds they get a text with your booking link.',
          },
          {
            iconKey: 'chat',
            title: 'Answers questions, day or night',
            desc: 'Hours, prices, directions, availability — handled on web and WhatsApp. You sleep, it sells.',
          },
          {
            iconKey: 'shield',
            title: 'Only real problems reach you',
            desc: 'Routine asks get handled automatically. You only see what actually needs you.',
          },
          {
            iconKey: 'bilingual',
            title: 'Sounds like your shop',
            desc: 'EN and 繁中, written by a native speaker. Customers feel like they\'re talking to your team.',
          },
        ],
        kpi: 'Lunch + dinner peaks → 0 lost callers',
        cta: 'See how this works →',
        visual: 'callback',
        imageAlt: 'Missed-call SMS auto-reply with chatbot panel · Pocky as policy-gate routing inbound to Auto / Confirm / Manual',
      },
      {
        id: 'reactivate',
        label: '1.3 · Database Reactivation',
        h: 'Dormant regulars come back without you asking.',
        features: [
          {
            iconKey: 'clock',
            title: 'Brought back at the right moment',
            desc: 'We message dormant customers right when they\'re ready — before they try the new place.',
          },
          {
            iconKey: 'target',
            title: 'Tuned to your business',
            desc: 'A salon, a gym, a restaurant — all run on different cycles. We tune yours from your data.',
          },
          {
            iconKey: 'chat',
            title: 'Sounds like you wrote it',
            desc: 'Built from your actual customer messages, in EN and 繁中. They reply. They don\'t unsubscribe.',
          },
          {
            iconKey: 'mail',
            title: 'One email every Monday',
            desc: 'Who came back, who slipped, who to nudge next. No dashboards. No logins. Just clarity.',
          },
        ],
        kpi: 'Reactivation 2 to 3× manual',
        cta: 'Wake up my list →',
        visual: 'reactivate',
        imageAlt: 'Customer database dashboard · dormant rows lighting up navy as reactivation SMS pings fire at the 60-day mark',
      },
      {
        id: 'reviews',
        label: '1.4 · Review Acquisition',
        h: 'Your Google profile starts working for you.',
        features: [
          {
            iconKey: 'star',
            title: 'Reviews come in on autopilot',
            desc: 'Every customer gets a friendly text the day after they visit. You don\'t lift a finger.',
          },
          {
            iconKey: 'pin',
            title: 'On the platforms locals check',
            desc: 'Google and OpenRice — exactly where HK customers look before deciding to walk in.',
          },
          {
            iconKey: 'shieldCheck',
            title: 'Compliant by design',
            desc: 'No filtering, no shady gating. Every review is real, every step audit-trailed.',
          },
          {
            iconKey: 'mail',
            title: 'One Monday email',
            desc: 'How many came in, your star rating, what changed. No dashboards. Just the numbers.',
          },
        ],
        kpi: '14 reviews → 60+ in 8 weeks',
        cta: 'Boost my reviews →',
        visual: 'reviews',
        imageAlt: 'Review feed mockup · Google + OpenRice 5-star reviews stacking up with weekly delta counter',
      },
    ],
  },
  2: {
    eyebrow: '§ 03 · Layer 2 · Digital Workforce',
    title: 'Digital employees we hire for your business.',
    sub: 'Once the foundation is live, we hire niche-specific digital employees with one job at your front desk. One per business. Trained on your tone, your customers, your operation.',
    cards: [
      {
        id: 'hair', label: 'Hair salon',
        role: 'Front-desk receptionist',
        h: 'Your front-desk Pocky.',
        oneLine: 'While you cut hair, Pocky books Saturday slots tuned per stylist, holds deposits on high-ticket colour, rebooks regulars 4 weeks out, and routes walk-ins to the next available chair.',
        kpi: 'Saturday slot-fill 68% → 94%',
        image: 'assets/pocky-work-hair-01.png',
        imageAlt: 'Pocky behind the reception desk of a Hong Kong hair salon, holding a tablet with a booking calendar.',
      },
      {
        id: 'medspa', label: 'Med-spa',
        role: 'Aesthetic coordinator',
        h: 'Your touch-up Pocky.',
        oneLine: 'Pocky reminds Botox clients at month-3 and hyaluron at month-6, holds deposits at booking, and reactivates lapsed treatments — bilingual EN + 繁中, every step audit-trailed.',
        kpi: 'No-show rate 18% → 4%',
        image: 'assets/pocky-work-medspa-02.png',
        imageAlt: 'Pocky at the front desk of a Hong Kong medical spa, holding a slim digital clipboard.',
      },
      {
        id: 'gym', label: 'Gym / fitness',
        role: 'Member-success agent',
        h: 'Your retention Pocky.',
        oneLine: 'Pocky catches lapsed members after three missed sessions, balances class-fill across trainers, runs the renewal flow two weeks before expiry, and onboards new joiners for 30 days.',
        kpi: 'Lapsed → 1 in 4 reactivated',
        image: 'assets/pocky-work-gym-01.png',
        imageAlt: 'Pocky behind the front desk of a Hong Kong boutique gym, looking at a class-roster touchscreen.',
      },
      {
        id: 'pet', label: 'Pet services',
        role: 'Grooming-cycle coordinator',
        h: 'Your grooming-cycle Pocky.',
        oneLine: 'Pocky tracks each dog\'s breed-specific grooming cadence, sends vaccination renewals tied to vet records, holds regular daycare slots, and posts a daily photo digest to owners.',
        kpi: 'Repeat grooming +37%',
        image: 'assets/pocky-work-pet-01.png',
        imageAlt: 'Pocky at a Hong Kong pet-grooming counter, next to a fluffy white Pomeranian on a soft mat.',
      },
    ],
  },
};

/* Pocky drawn as inline SVG so we can animate its parts (eyes blink, antenna pulse, body bob).
   Use this anywhere we want a "live" Pocky instead of the flat png. */
const PockyAnimated = ({ size = 84, mood = 'work', className = '' }) => (
  <svg
    viewBox="0 0 100 100"
    width={size}
    height={size}
    className={`pocky-svg pocky-mood-${mood} ${className}`}
    aria-hidden="true"
  >
    {/* antenna with pulsing tip */}
    <g className="pocky-antenna">
      <line x1="50" y1="10" x2="50" y2="22" stroke="var(--pc-ink)" strokeWidth="1.4" strokeLinecap="round" />
      <circle cx="50" cy="9" r="2.6" fill="var(--pc-orange)" className="pocky-antenna-tip" />
    </g>
    {/* ears */}
    <ellipse cx="22" cy="32" rx="6" ry="9" fill="var(--pc-ink)" transform="rotate(-18 22 32)" />
    <ellipse cx="78" cy="32" rx="6" ry="9" fill="var(--pc-ink)" transform="rotate(18 78 32)" />
    {/* head */}
    <ellipse cx="50" cy="48" rx="30" ry="28" fill="var(--pc-off-white)" stroke="var(--pc-ink)" strokeWidth="2" />
    {/* visor band */}
    <path d="M 25 42 Q 50 36 75 42 L 75 50 Q 50 46 25 50 Z" fill="var(--pc-ink)" />
    {/* eyes — animated via CSS scaleY for blinks */}
    <g className="pocky-eyes">
      <circle cx="40" cy="46" r="2.4" fill="var(--pc-orange)" />
      <circle cx="60" cy="46" r="2.4" fill="var(--pc-orange)" />
    </g>
    {/* mouth */}
    <path d="M 44 60 Q 50 64 56 60" stroke="var(--pc-ink)" strokeWidth="1.8" fill="none" strokeLinecap="round" />
    {/* body band */}
    <rect x="36" y="76" width="28" height="6" rx="3" fill="var(--pc-orange)" />
    {/* feet */}
    <ellipse cx="42" cy="88" rx="5" ry="3" fill="var(--pc-ink)" />
    <ellipse cx="58" cy="88" rx="5" ry="3" fill="var(--pc-ink)" />
  </svg>
);

/* Feature icons · custom SVG line-icons for Layer 1 feature grids.
   24x24 viewBox, currentColor stroke 1.6, line-cap round. */
const FeatureIcon = ({ iconKey }) => {
  const stroke = { fill: 'none', stroke: 'currentColor', strokeWidth: 1.6, strokeLinecap: 'round', strokeLinejoin: 'round' };
  const icons = {
    seo: (
      <svg viewBox="0 0 24 24" {...stroke} aria-hidden="true">
        <circle cx="11" cy="11" r="6.5" />
        <line x1="20" y1="20" x2="16" y2="16" />
        <path d="M11 14V8" />
        <path d="M8 11l3-3 3 3" />
      </svg>
    ),
    lighthouse: (
      <svg viewBox="0 0 24 24" {...stroke} aria-hidden="true">
        <path d="M3.5 16a8.5 8.5 0 0 1 17 0" />
        <line x1="12" y1="16" x2="17" y2="9.5" />
        <circle cx="12" cy="16" r="1.6" fill="currentColor" stroke="none" />
        <line x1="6" y1="20" x2="18" y2="20" />
      </svg>
    ),
    speed: (
      <svg viewBox="0 0 24 24" {...stroke} aria-hidden="true">
        <path d="M13 2 4 13.5h7L10 22l9-11.5h-7L13 2z" />
      </svg>
    ),
    bilingual: (
      <svg viewBox="0 0 24 24" {...stroke} aria-hidden="true">
        <circle cx="12" cy="12" r="9" />
        <path d="M3 12h18" />
        <path d="M12 3a13.5 13.5 0 0 1 0 18" />
        <path d="M12 3a13.5 13.5 0 0 0 0 18" />
      </svg>
    ),
    clock: (
      <svg viewBox="0 0 24 24" {...stroke} aria-hidden="true">
        <circle cx="12" cy="12" r="9" />
        <polyline points="12 7 12 12 16 14" />
      </svg>
    ),
    chat: (
      <svg viewBox="0 0 24 24" {...stroke} aria-hidden="true">
        <path d="M21 12a8 8 0 0 1-8 8H7l-4 3v-7a8 8 0 0 1 8-8h2a8 8 0 0 1 8 4z" />
        <circle cx="9" cy="12.5" r="1" fill="currentColor" stroke="none" />
        <circle cx="13" cy="12.5" r="1" fill="currentColor" stroke="none" />
        <circle cx="17" cy="12.5" r="1" fill="currentColor" stroke="none" />
      </svg>
    ),
    shield: (
      <svg viewBox="0 0 24 24" {...stroke} aria-hidden="true">
        <path d="M12 3 4 6v6c0 4.5 3.4 8.4 8 9 4.6-.6 8-4.5 8-9V6l-8-3z" />
      </svg>
    ),
    shieldCheck: (
      <svg viewBox="0 0 24 24" {...stroke} aria-hidden="true">
        <path d="M12 3 4 6v6c0 4.5 3.4 8.4 8 9 4.6-.6 8-4.5 8-9V6l-8-3z" />
        <polyline points="9 12 11.5 14.5 16 10" />
      </svg>
    ),
    target: (
      <svg viewBox="0 0 24 24" {...stroke} aria-hidden="true">
        <circle cx="12" cy="12" r="9" />
        <circle cx="12" cy="12" r="5" />
        <circle cx="12" cy="12" r="1.5" fill="currentColor" stroke="none" />
      </svg>
    ),
    mail: (
      <svg viewBox="0 0 24 24" {...stroke} aria-hidden="true">
        <rect x="3" y="5" width="18" height="14" rx="2" />
        <polyline points="3 7 12 13 21 7" />
      </svg>
    ),
    star: (
      <svg viewBox="0 0 24 24" {...stroke} aria-hidden="true">
        <polygon points="12 3 14.7 9.5 21.5 10 16.4 14.5 18 21 12 17.5 6 21 7.6 14.5 2.5 10 9.3 9.5 12 3" />
      </svg>
    ),
    pin: (
      <svg viewBox="0 0 24 24" {...stroke} aria-hidden="true">
        <path d="M12 22s7-7 7-12a7 7 0 1 0-14 0c0 5 7 12 7 12z" />
        <circle cx="12" cy="10" r="2.5" />
      </svg>
    ),
    calendar: (
      <svg viewBox="0 0 24 24" {...stroke} aria-hidden="true">
        <rect x="3" y="5" width="18" height="16" rx="2" />
        <line x1="3" y1="10" x2="21" y2="10" />
        <line x1="8" y1="3" x2="8" y2="7" />
        <line x1="16" y1="3" x2="16" y2="7" />
      </svg>
    ),
    repeat: (
      <svg viewBox="0 0 24 24" {...stroke} aria-hidden="true">
        <polyline points="17 4 21 8 17 12" />
        <path d="M3 11V9a4 4 0 0 1 4-4h14" />
        <polyline points="7 20 3 16 7 12" />
        <path d="M21 13v2a4 4 0 0 1-4 4H3" />
      </svg>
    ),
    card: (
      <svg viewBox="0 0 24 24" {...stroke} aria-hidden="true">
        <rect x="2" y="6" width="20" height="14" rx="2" />
        <line x1="2" y1="11" x2="22" y2="11" />
        <line x1="6" y1="16" x2="10" y2="16" />
      </svg>
    ),
  };
  return <span className="pc-feature-icon">{icons[iconKey] || null}</span>;
};

/* Layer 1.1 — Smart Website. 6s GSAP storyloop: ranks → fast → converts.
   Authored under hyperframes motion-principles (varied eases, build/breathe/resolve, IO-gated). */
const SmartWebsiteVisual = () => {
  const rootRef = useRef(null);

  useEffect(() => {
    const root = rootRef.current;
    const gsap = window.gsap;
    if (!root || !gsap) return;
    if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;

    const q = (sel) => root.querySelector(sel);
    const arc = q('.sw-gauge-arc');
    const numEl = q('.sw-gauge-num');
    const ARC_LEN = 263.9;          // 2π·42, full circumference
    const ARC_TARGET = ARC_LEN * 0.78; // ~22% fill — Lighthouse-style "fast"
    const numState = { val: 4.0 };

    const tl = gsap.timeline({
      paused: true,
      repeat: -1,
      repeatDelay: 0.4,
      defaults: { ease: 'power2.out' },
    });

    const rankEl = q('.sw-snippet-rank-num');
    const rankSteps = [47, 23, 8, 3, 1];

    // RESET — every loop starts from clean state
    tl.set('.sw-snippet', { autoAlpha: 0, x: -28, y: -16 });
    tl.set('.sw-snippet-star', { autoAlpha: 0, scale: 0.4, transformOrigin: 'center center' });
    tl.set('.sw-snippet-rank-trend', { autoAlpha: 0, x: -6, scale: 0.8 });
    tl.set('.sw-browser', { autoAlpha: 1, scale: 0.96, transformOrigin: 'center center' });
    tl.set('.sw-headline', { scaleX: 0, transformOrigin: 'left center' });
    tl.set('.sw-line', { scaleX: 0, transformOrigin: 'left center' });
    tl.set('.sw-cta', { scale: 0.7, autoAlpha: 0 });
    tl.set('.sw-pulse-ring', { scale: 0.5, autoAlpha: 0 });
    tl.set('.sw-toast', { autoAlpha: 0, y: 28 });
    tl.set('.sw-pocky-bubble', { autoAlpha: 0, y: 6, scale: 0.9 });
    tl.set('.sw-gauge-badge', { autoAlpha: 0, y: -4, scale: 0.8, xPercent: -50, transformOrigin: 'center top' });
    tl.set(arc, { strokeDashoffset: ARC_LEN });
    tl.call(() => {
      numState.val = 4.0;
      if (numEl) numEl.textContent = '4.0';
      if (rankEl) rankEl.textContent = String(rankSteps[0]);
    });

    // PHASE 1 — RANKS (0.2 → 1.4s). Browser + Google snippet enter while
    // the rank counter climbs #47 → #1 in stepped jumps. Stars fan in last,
    // trending pill pops on top spot. Pocky bubble runs in parallel.
    tl.to('.sw-browser', { scale: 1, duration: 0.55, ease: 'back.out(1.4)' }, 0.2);
    tl.to('.sw-snippet', { autoAlpha: 1, x: 0, y: 0, duration: 0.5, ease: 'expo.out' }, 0.4);
    tl.to('.sw-bubble-rank', { autoAlpha: 1, y: 0, scale: 1, duration: 0.4, ease: 'back.out(1.6)' }, 0.6);

    // Rank-climb counter: #47 → #23 → #8 → #3 → #1 (stepped jumps).
    // Each tick + flash communicates real SERP movement, not a static "#1".
    rankSteps.slice(1).forEach((r, i) => {
      const t = 0.65 + i * 0.18;
      tl.call(() => { if (rankEl) rankEl.textContent = String(r); }, null, t);
      tl.fromTo('.sw-snippet-rank',
        { scale: 1.18, backgroundColor: 'rgba(215, 98, 15, 0.22)' },
        { scale: 1, backgroundColor: 'rgba(215, 98, 15, 0.08)', duration: 0.22, ease: 'power2.out' },
        t
      );
    });
    // Stars fan in once rank is locked at #1
    tl.to('.sw-snippet-star', { autoAlpha: 1, scale: 1, duration: 0.28, stagger: 0.06, ease: 'back.out(1.8)' }, 1.4);
    tl.to('.sw-snippet-rank-trend', { autoAlpha: 1, x: 0, scale: 1, duration: 0.32, ease: 'back.out(2)' }, 1.55);

    // PHASE 2 — FAST (1.5 → 2.7s). Site paints, gauge counts down digitally
    // in 6 stepped jumps (4.0 → 1.2), arc fills in sync, then number scale-pops
    // and a "✓ FAST" badge slides out below the dial.
    tl.to('.sw-headline', { scaleX: 1, duration: 0.42, ease: 'power3.out' }, 1.5);
    tl.to('.sw-line.w70', { scaleX: 1, duration: 0.32 }, 1.65);
    tl.to('.sw-line.w55', { scaleX: 1, duration: 0.30 }, 1.78);
    tl.to(arc, { strokeDashoffset: ARC_TARGET, duration: 0.85, ease: 'steps(6)' }, 1.6);
    tl.to(numState, {
      val: 1.2,
      duration: 0.85,
      ease: 'steps(6)',
      onUpdate: () => { if (numEl) numEl.textContent = numState.val.toFixed(1); },
    }, 1.6);
    tl.fromTo('.sw-gauge-num',
      { scale: 1 },
      { scale: 1.22, duration: 0.18, ease: 'power2.out', yoyo: true, repeat: 1 },
      2.45
    );
    tl.fromTo('.sw-gauge-badge',
      { autoAlpha: 0, y: -4, scale: 0.8, xPercent: -50 },
      { autoAlpha: 1, y: 0, scale: 1, xPercent: -50, duration: 0.38, ease: 'back.out(1.8)' },
      2.5
    );
    tl.to('.sw-bubble-rank', { autoAlpha: 0, y: -6, duration: 0.3, ease: 'power2.in' }, 1.4);
    tl.fromTo('.sw-bubble-fast',
      { autoAlpha: 0, y: 6, scale: 0.9 },
      { autoAlpha: 1, y: 0, scale: 1, duration: 0.4, ease: 'back.out(1.6)' },
      1.7
    );

    // PHASE 3 — CONVERTS (3.0 → 4.4s). CTA appears, pulses, booking confirms.
    tl.to('.sw-cta', { scale: 1, autoAlpha: 1, duration: 0.32, ease: 'back.out(1.6)' }, 3.0);
    tl.fromTo('.sw-pulse-ring',
      { scale: 0.9, autoAlpha: 0.55 },
      { scale: 1.7, autoAlpha: 0, duration: 0.7, ease: 'power2.out' },
      3.45
    );
    tl.to('.sw-toast', { autoAlpha: 1, y: 0, duration: 0.5, ease: 'back.out(1.5)' }, 3.7);
    tl.to('.sw-bubble-fast', { autoAlpha: 0, y: -6, duration: 0.3, ease: 'power2.in' }, 3.5);
    tl.fromTo('.sw-bubble-book',
      { autoAlpha: 0, y: 6, scale: 0.9 },
      { autoAlpha: 1, y: 0, scale: 1, duration: 0.42, ease: 'back.out(1.6)' },
      3.85
    );

    // PHASE 4 — RESOLVE (5.0 → 5.6s). Snippet + toast + bubble fade, CTA dims, then loop.
    tl.to(['.sw-snippet', '.sw-toast'], { autoAlpha: 0, duration: 0.35, ease: 'power2.in' }, 5.0);
    tl.to('.sw-bubble-book', { autoAlpha: 0, y: -6, duration: 0.3, ease: 'power2.in' }, 5.0);
    tl.to('.sw-gauge-badge', { autoAlpha: 0, duration: 0.3, ease: 'power2.in' }, 5.0);
    tl.to('.sw-cta', { autoAlpha: 0.4, scale: 0.95, duration: 0.35, ease: 'power2.in' }, 5.1);
    tl.to({}, { duration: 0.3 }, 5.6);

    // IO gate — only animate when section is on-screen (saves CPU on long page).
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => { if (e.isIntersecting) tl.play(); else tl.pause(); });
    }, { threshold: 0.2 });
    io.observe(root);

    return () => {
      io.disconnect();
      tl.kill();
    };
  }, []);

  return (
    <div className="pc-visual-card vis-site" ref={rootRef}>
      <div className="pc-visual-meta">Smart Website · live preview</div>
      <div className="pc-visual-stage">
        {/* Google search snippet — "ranks" beat. Rank-climb counter (#47→#1),
           star-fill stagger, and trending pill make the SEO story visible. */}
        <div className="sw-snippet" aria-hidden="true">
          <div className="sw-snippet-eyebrow">Google · SERP</div>
          <div className="sw-snippet-rank-row">
            <span className="sw-snippet-rank">#<span className="sw-snippet-rank-num">47</span></span>
            <span className="sw-snippet-rank-trend">▲ trending</span>
          </div>
          <div className="sw-snippet-url">your-salon.hk</div>
          <div className="sw-snippet-title">Best hair salon · Sheung Wan</div>
          <div className="sw-snippet-stars">
            <span className="sw-snippet-star">★</span>
            <span className="sw-snippet-star">★</span>
            <span className="sw-snippet-star">★</span>
            <span className="sw-snippet-star">★</span>
            <span className="sw-snippet-star">★</span>
            <small>4.9 · Open now · Book online</small>
          </div>
        </div>

        {/* Browser frame */}
        <div className="sw-browser">
          <div className="sw-browser-bar">
            <span></span><span></span><span></span>
            <div className="sw-browser-url">your-salon.hk</div>
          </div>
          <div className="sw-browser-content">
            <div className="sw-headline"></div>
            <div className="sw-line w70"></div>
            <div className="sw-line w55"></div>
            <div className="sw-cta-wrap">
              <div className="sw-cta">Book now · 立即預約 →</div>
              <div className="sw-pulse-ring" aria-hidden="true"></div>
            </div>
            <div className="sw-langs"><span className="active">EN</span><span>繁中</span></div>
          </div>
        </div>

        {/* Lighthouse gauge — speedometer dial. Tick-marks around the rim,
           stepped digital countdown, scale-pop + badge on lock. */}
        <div className="sw-gauge" aria-hidden="true">
          <svg viewBox="0 0 100 100">
            <circle cx="50" cy="50" r="42" fill="none" stroke="var(--pc-pale-silver)" strokeWidth="6" />
            {Array.from({ length: 12 }).map((_, i) => (
              <line
                key={i}
                x1="50" y1="2.5" x2="50" y2={i % 3 === 0 ? 7.5 : 6}
                stroke={i % 3 === 0 ? 'var(--pc-pewter)' : 'var(--pc-pale-silver)'}
                strokeWidth={i % 3 === 0 ? 1.6 : 1.1}
                strokeLinecap="round"
                transform={`rotate(${i * 30} 50 50)`}
              />
            ))}
            <circle
              className="sw-gauge-arc"
              cx="50" cy="50" r="42"
              fill="none"
              stroke="var(--pc-orange)"
              strokeWidth="6"
              strokeDasharray="263.9"
              strokeDashoffset="263.9"
              transform="rotate(-90 50 50)"
              strokeLinecap="round"
            />
          </svg>
          <div className="sw-gauge-label">
            <strong className="sw-gauge-num">4.0</strong>
            <span>load · sec</span>
          </div>
          <div className="sw-gauge-badge">✓ FAST</div>
        </div>

        {/* Booked toast — "converts" beat. Brand-pack Pocky (canonical) replaces
           generic check icon — makes AI-agent identity explicit and on-brand. */}
        <div className="sw-toast" aria-hidden="true">
          <div className="sw-toast-pocky">
            <img src="assets/pocky-working.png" alt="" className="sw-pocky-img" />
          </div>
          <div>
            <strong>Booked · Sat 14:00</strong>
            <small>via Pocky · your-salon.hk</small>
          </div>
        </div>

        {/* Pocky bottom-right · the AI agent. Brand-pack PNG (canonical Pocky)
           with cycling thought-bubbles so he reads as active worker, not static. */}
        <div className="vis-pocky-anchor pos-br sw-pocky-anchor">
          <div className="sw-pocky-bubbles">
            <div className="sw-pocky-bubble sw-bubble-rank">indexing…</div>
            <div className="sw-pocky-bubble sw-bubble-fast">1.2s ✓</div>
            <div className="sw-pocky-bubble sw-bubble-book">booked ✓</div>
          </div>
          <img src="assets/pocky-working.png" alt="" className="sw-pocky-img sw-pocky-corner" />
        </div>
      </div>
    </div>
  );
};

/* Layer 1.2 — Missed-Call Text-Back. 6s GSAP storyloop: ring → SLA-countdown
   → typing → SMS sent → customer books. Same principles as 1.1 (stepped digital
   readout, sequential reveals, lock-in badges, canonical Pocky in the corner). */
const MissedCallVisual = () => {
  const rootRef = useRef(null);

  useEffect(() => {
    const root = rootRef.current;
    const gsap = window.gsap;
    if (!root || !gsap) return;
    if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;

    const q = (sel) => root.querySelector(sel);
    const numEl = q('.cb-timer-num');
    const barEl = q('.cb-timer-bar-fill');
    const numState = { val: 30 };

    const tl = gsap.timeline({
      paused: true,
      repeat: -1,
      repeatDelay: 0.4,
      defaults: { ease: 'power2.out' },
    });

    // RESET — every loop starts from clean state
    tl.set('.cb-missed', { autoAlpha: 0, y: -16 });
    tl.set('.cb-avatar', { scale: 0, transformOrigin: 'center center' });
    tl.set('.cb-ring-pulse', { autoAlpha: 0, scale: 0.6 });
    tl.set('.cb-x', { scale: 0, rotation: -45, transformOrigin: 'center center' });
    tl.set('.cb-typing', { autoAlpha: 0, scale: 0.85, transformOrigin: 'left center' });
    tl.set('.cb-sms', { autoAlpha: 0, y: 14, scale: 0.92, transformOrigin: 'left bottom' });
    tl.set('.cb-reply', { autoAlpha: 0, y: 14, scale: 0.92, transformOrigin: 'right bottom' });
    tl.set('.cb-pill-replied', { autoAlpha: 0, y: -4, scale: 0.8, xPercent: -50, transformOrigin: 'center top' });
    tl.set('.cb-pill-booked', { autoAlpha: 0, y: -4, scale: 0.8, xPercent: -50, transformOrigin: 'center top' });
    tl.set(barEl, { scaleX: 1, transformOrigin: 'left center' });
    tl.call(() => {
      numState.val = 30;
      if (numEl) numEl.textContent = '30';
    });

    // PHASE 1 — RING (0.2 → 1.4s). Card slides in, avatar pops, ring pulses
    // expand from the avatar (call coming in), then the orange × slams in
    // and shakes — call dropped to missed.
    tl.to('.cb-missed', { autoAlpha: 1, y: 0, duration: 0.5, ease: 'expo.out' }, 0.2);
    tl.to('.cb-avatar', { scale: 1, duration: 0.4, ease: 'back.out(1.8)' }, 0.35);
    tl.fromTo('.cb-ring-pulse',
      { scale: 0.6, autoAlpha: 0.55 },
      { scale: 2.0, autoAlpha: 0, duration: 0.8, ease: 'power2.out', stagger: 0.22 },
      0.5
    );
    tl.to('.cb-x', { scale: 1, rotation: 0, duration: 0.32, ease: 'back.out(2)' }, 1.05);
    tl.to('.cb-x', {
      keyframes: { rotation: [0, -12, 10, -7, 0] },
      duration: 0.5, ease: 'power2.inOut',
    }, 1.15);

    // PHASE 2 — SLA COUNTDOWN (1.5 → 2.6s). Digital readout ticks 30 → 24 → 18
    // → 12 → 06 (steps(4) ease, stepped jumps), bar depletes in sync. Same
    // digital-meter feel as the gauge in Animation 1.1.
    tl.to(numState, {
      val: 6,
      duration: 1.0,
      ease: 'steps(4)',
      onUpdate: () => {
        if (numEl) numEl.textContent = String(Math.max(0, Math.round(numState.val))).padStart(2, '0');
      },
    }, 1.5);
    tl.to(barEl, { scaleX: 0.2, duration: 1.0, ease: 'steps(4)' }, 1.5);

    // PHASE 3 — TYPING (2.6 → 3.2s). Pocky composing — three pulsing dots in
    // an iMessage-style bubble, anchored left.
    tl.to('.cb-typing', { autoAlpha: 1, scale: 1, duration: 0.32, ease: 'back.out(1.6)' }, 2.6);

    // PHASE 4 — SMS SENT (3.2 → 4.3s). Typing fades, outgoing SMS bubble
    // slides up (navy iMessage-style with rounded corner). Timer locks, scale-
    // pop on number, "✓ REPLIED" pill slides out below the timer.
    tl.to('.cb-typing', { autoAlpha: 0, scale: 0.85, duration: 0.25, ease: 'power2.in' }, 3.15);
    tl.to('.cb-sms', { autoAlpha: 1, y: 0, scale: 1, duration: 0.45, ease: 'back.out(1.5)' }, 3.25);
    tl.fromTo('.cb-timer-num',
      { scale: 1 },
      { scale: 1.18, duration: 0.18, ease: 'power2.out', yoyo: true, repeat: 1 },
      3.4
    );
    tl.fromTo('.cb-pill-replied',
      { autoAlpha: 0, y: -4, scale: 0.8, xPercent: -50 },
      { autoAlpha: 1, y: 0, scale: 1, xPercent: -50, duration: 0.38, ease: 'back.out(1.8)' },
      3.5
    );

    // PHASE 5 — CUSTOMER BOOKS (4.4 → 5.3s). Incoming reply bubble slides
    // in from the right (light card style — incoming, opposite the outgoing
    // navy SMS). REPLIED pill swaps to "✓ BOOKED" orange pill.
    tl.to('.cb-reply', { autoAlpha: 1, y: 0, scale: 1, duration: 0.45, ease: 'back.out(1.5)' }, 4.4);
    tl.to('.cb-pill-replied', { autoAlpha: 0, y: -4, duration: 0.3, ease: 'power2.in' }, 4.5);
    tl.fromTo('.cb-pill-booked',
      { autoAlpha: 0, y: -4, scale: 0.8, xPercent: -50 },
      { autoAlpha: 1, y: 0, scale: 1, xPercent: -50, duration: 0.4, ease: 'back.out(1.8)' },
      4.6
    );

    // PHASE 6 — RESOLVE (5.3 → 5.9s). Dim everything, then loop.
    tl.to(['.cb-missed', '.cb-sms', '.cb-reply'], { autoAlpha: 0.4, duration: 0.4, ease: 'power2.in' }, 5.3);
    tl.to('.cb-pill-booked', { autoAlpha: 0, duration: 0.3, ease: 'power2.in' }, 5.3);
    tl.to({}, { duration: 0.3 }, 5.8);

    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => { if (e.isIntersecting) tl.play(); else tl.pause(); });
    }, { threshold: 0.2 });
    io.observe(root);

    return () => {
      io.disconnect();
      tl.kill();
    };
  }, []);

  return (
    <div className="pc-visual-card vis-callback" ref={rootRef}>
      <div className="pc-visual-meta">Missed call · auto-reply &lt;30s</div>
      <div className="pc-visual-stage">
        {/* Vertical message thread — missed call → typing → outgoing SMS → customer reply */}
        <div className="cb-thread">
          <div className="cb-missed">
            <div className="cb-avatar-wrap">
              <span className="cb-ring-pulse"></span>
              <span className="cb-ring-pulse"></span>
              <span className="cb-avatar">SC</span>
            </div>
            <div className="cb-missed-body">
              <strong>Missed call · Sarah C.</strong>
              <small>+852 5587 · 14:02</small>
            </div>
            <span className="cb-x" aria-hidden="true">×</span>
          </div>

          <div className="cb-typing" aria-hidden="true">
            <span></span><span></span><span></span>
          </div>

          <div className="cb-sms">
            <strong>Sorry we missed you. Book here: your-salon.hk/book</strong>
            <small>Sent · 14:02:18 ✓✓</small>
          </div>

          <div className="cb-reply">
            <span className="cb-reply-avatar">SC</span>
            <div>
              <strong>Booked · Sat 14:00</strong>
              <small>via your-salon.hk · 14:02:42</small>
            </div>
          </div>
        </div>

        {/* SLA widget — top-right. Digital countdown + depleting bar.
           "✓ REPLIED" / "✓ BOOKED" pills slide out below it as phases progress. */}
        <div className="cb-sla" aria-hidden="true">
          <div className="cb-timer">
            <div className="cb-timer-eyebrow">Reply in</div>
            <strong className="cb-timer-num">30</strong>
            <span className="cb-timer-unit">sec</span>
            <div className="cb-timer-bar"><span className="cb-timer-bar-fill"></span></div>
          </div>
          <div className="cb-pill-slot">
            <div className="cb-pill cb-pill-replied">✓ REPLIED</div>
            <div className="cb-pill cb-pill-booked">✓ BOOKED</div>
          </div>
        </div>

        {/* Pocky bottom-right — canonical brand-pack PNG, same bob as 1.1 */}
        <div className="vis-pocky-anchor pos-br cb-pocky-anchor">
          <img src="assets/pocky-working.png" alt="" className="sw-pocky-img sw-pocky-corner" />
        </div>
      </div>
    </div>
  );
};

/* Layer 1.3 — Database Reactivation. 6s GSAP storyloop: audit → outreach
   → wakeup → lock. Same principles as 1.1 / 1.2 (data widget top-right,
   stepped digital readout, sequential reveals, lock-in pill, canonical
   Pocky in the corner). */
const REACT_BOOKED_INDICES = [1, 5, 9, 12, 16, 19, 22, 26]; // 8 of 28, scattered across the 7×4 grid

const ReactivateVisual = () => {
  const rootRef = useRef(null);

  useEffect(() => {
    const root = rootRef.current;
    const gsap = window.gsap;
    if (!root || !gsap) return;
    if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;

    const q = (sel) => root.querySelector(sel);
    const counterEl = q('.re-counter-num');
    const barEl = q('.re-counter-bar-fill');
    const TOTAL_BOOKED = REACT_BOOKED_INDICES.length;
    const BAR_TARGET = TOTAL_BOOKED / 23; // 8/23 ≈ 0.348

    const tl = gsap.timeline({
      paused: true,
      repeat: -1,
      repeatDelay: 0.4,
      defaults: { ease: 'power2.out' },
    });

    // RESET — clean state every loop
    tl.set('.re-dot', {
      autoAlpha: 0, scale: 0.4,
      backgroundColor: '#D0D1D2',
      boxShadow: 'none',
      transformOrigin: 'center center',
    });
    tl.set('.re-dot-ping', { autoAlpha: 0, scale: 0.5, transformOrigin: 'center center' });
    tl.set('.re-counter-widget', { autoAlpha: 0, x: 8 });
    tl.set(barEl, { scaleX: 0, transformOrigin: 'left center' });
    tl.set('.re-pill', { autoAlpha: 0, y: -4, scale: 0.8, xPercent: -50, transformOrigin: 'center top' });
    tl.call(() => { if (counterEl) counterEl.textContent = '0'; });

    // PHASE 1 — AUDIT (0.2 → 1.0s). Dots fade-pop into the grid (random stagger
    // so they materialise like a database scan), counter widget slides in.
    tl.to('.re-dot', {
      autoAlpha: 1, scale: 1,
      duration: 0.4,
      ease: 'back.out(1.5)',
      stagger: { each: 0.022, from: 'random' },
    }, 0.2);
    tl.to('.re-counter-widget', { autoAlpha: 1, x: 0, duration: 0.4, ease: 'expo.out' }, 0.4);

    // PHASE 2 — OUTREACH (1.1 → 2.4s). Navy ping ring expands from each dot in
    // a left-to-right wave. Visualises Pocky pinging every dormant contact.
    tl.fromTo('.re-dot-ping',
      { scale: 0.5, autoAlpha: 0.6 },
      {
        scale: 2.4, autoAlpha: 0,
        duration: 0.55, ease: 'power2.out',
        stagger: { each: 0.04, from: 'start' },
      },
      1.1
    );

    // PHASE 3 — WAKEUP (2.5 → 3.7s). 8 of 28 dots flip orange (booked back),
    // staggered 0.15s apart. Each flip ticks the counter 0 → 8 in sync, the
    // bar fills 0 → 8/23 in stepped jumps.
    REACT_BOOKED_INDICES.forEach((idx, i) => {
      const t = 2.5 + i * 0.15;
      tl.to(`.re-dot[data-i="${idx}"]`, {
        backgroundColor: '#D7620F',
        boxShadow: '0 0 10px rgba(215, 98, 15, 0.45)',
        duration: 0.28, ease: 'power2.out',
      }, t);
      tl.fromTo(`.re-dot[data-i="${idx}"]`,
        { scale: 1 },
        { scale: 1.55, duration: 0.18, ease: 'back.out(2)', yoyo: true, repeat: 1 },
        t
      );
      tl.call(() => { if (counterEl) counterEl.textContent = String(i + 1); }, null, t);
    });
    tl.to(barEl, {
      scaleX: BAR_TARGET,
      duration: TOTAL_BOOKED * 0.15,
      ease: `steps(${TOTAL_BOOKED})`,
    }, 2.5);

    // PHASE 4 — LOCK (3.8 → 4.3s). Counter scale-pops, "✓ 8 BOOKED BACK" pill
    // slides out below the widget — same lock-in pattern as 1.1 (FAST) and 1.2
    // (REPLIED → BOOKED).
    tl.fromTo('.re-counter-num',
      { scale: 1 },
      { scale: 1.22, duration: 0.18, ease: 'power2.out', yoyo: true, repeat: 1 },
      3.85
    );
    tl.fromTo('.re-pill',
      { autoAlpha: 0, y: -4, scale: 0.8, xPercent: -50 },
      { autoAlpha: 1, y: 0, scale: 1, xPercent: -50, duration: 0.4, ease: 'back.out(1.8)' },
      3.95
    );

    // PHASE 5 — RESOLVE (4.6 → 5.4s). Pill fades, dim everything, hold, loop.
    tl.to('.re-pill', { autoAlpha: 0, duration: 0.3, ease: 'power2.in' }, 4.7);
    tl.to({}, { duration: 0.5 }, 5.2);

    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => { if (e.isIntersecting) tl.play(); else tl.pause(); });
    }, { threshold: 0.2 });
    io.observe(root);

    return () => {
      io.disconnect();
      tl.kill();
    };
  }, []);

  return (
    <div className="pc-visual-card vis-reactivate" ref={rootRef}>
      <div className="pc-visual-meta">Dormant 60 to 90 days · re-engaging</div>
      <div className="pc-visual-stage">
        {/* 7×4 dot grid — each dot is one dormant contact. 8 flip orange when booked back. */}
        <div className="re-grid">
          {Array.from({ length: 28 }).map((_, i) => (
            <span key={i} className="re-dot" data-i={i}>
              <span className="re-dot-ping" aria-hidden="true"></span>
            </span>
          ))}
        </div>

        {/* Counter widget — top-right, mirrors gauge / SLA timer in 1.1 / 1.2 */}
        <div className="re-counter-widget" aria-hidden="true">
          <div className="re-counter">
            <div className="re-counter-eyebrow">Booked back</div>
            <strong className="re-counter-num">0</strong>
            <span className="re-counter-unit">of 23</span>
            <div className="re-counter-bar"><span className="re-counter-bar-fill"></span></div>
          </div>
          <div className="re-pill-slot">
            <div className="re-pill">✓ 8 BOOKED BACK</div>
          </div>
        </div>

        {/* Pocky bottom-right — canonical brand-pack PNG, same bob class as 1.1 / 1.2 */}
        <div className="vis-pocky-anchor pos-br re-pocky-anchor">
          <img src="assets/pocky-working.png" alt="" className="sw-pocky-img sw-pocky-corner" />
        </div>
      </div>
    </div>
  );
};

/* Layer 1.4 — Review Acquisition. ~5.6s GSAP storyloop: 5 stars fill →
   counter climbs 14 → 62 with floating +1★ pulses → ✓ 4.9 RATING locks in.
   Same principles as 1.1-1.3 but the stars+counter are the centered hero
   (no side widget — this card is the celebratory social-proof beat). */
const ReviewsVisual = () => {
  const rootRef = useRef(null);

  useEffect(() => {
    const root = rootRef.current;
    const gsap = window.gsap;
    if (!root || !gsap) return;
    if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;

    const q = (sel) => root.querySelector(sel);
    const numEl = q('.rev-counter-num');
    const numState = { val: 14 };

    const tl = gsap.timeline({
      paused: true,
      repeat: -1,
      repeatDelay: 0.4,
      defaults: { ease: 'power2.out' },
    });

    // RESET — every loop starts clean
    tl.set('.rev-star', {
      autoAlpha: 0, scale: 0.4, color: '#D0D1D2',
      transformOrigin: 'center center',
    });
    tl.set('.rev-counter-num', { autoAlpha: 0, y: 6, scale: 1 });
    tl.set('.rev-counter-caption', { autoAlpha: 0, y: 4 });
    tl.set('.rev-pulse', { autoAlpha: 0, y: 14, scale: 0.85 });
    tl.set('.rev-pill', { autoAlpha: 0, y: -4, scale: 0.8, xPercent: -50, transformOrigin: 'center top' });
    tl.call(() => {
      numState.val = 14;
      if (numEl) numEl.textContent = '14';
    });

    // PHASE 1 — STAR FILL (0.3 → 1.4s). 5 stars stagger-pop in left-to-right,
    // colour tweens from grey to orange with a satisfying back.out overshoot.
    tl.to('.rev-star', {
      autoAlpha: 1, scale: 1, color: '#D7620F',
      duration: 0.32, ease: 'back.out(2.5)',
      stagger: 0.14,
    }, 0.3);

    // PHASE 2 — COUNTER CLIMB (1.3 → 3.0s). Counter fades in, then ticks
    // 14 → 62 over 1.5s with power2.out (decelerating tally feel — like a real
    // review feed snapping to total).
    tl.to('.rev-counter-num', { autoAlpha: 1, y: 0, duration: 0.32, ease: 'expo.out' }, 1.3);
    tl.to('.rev-counter-caption', { autoAlpha: 1, y: 0, duration: 0.32, ease: 'expo.out' }, 1.4);
    tl.to(numState, {
      val: 62,
      duration: 1.5,
      ease: 'power2.out',
      onUpdate: () => { if (numEl) numEl.textContent = String(Math.floor(numState.val)); },
    }, 1.5);

    // +1 ★ pulses — 3 of them float up and fade during the climb, scattered
    // around the upper area so they read as "incoming reviews".
    [0, 1, 2].forEach((i) => {
      const t = 1.7 + i * 0.42;
      tl.fromTo(`.rev-pulse-${i + 1}`,
        { autoAlpha: 0, y: 16, scale: 0.85 },
        { autoAlpha: 1, y: 0, scale: 1, duration: 0.3, ease: 'back.out(1.8)' },
        t
      );
      tl.to(`.rev-pulse-${i + 1}`,
        { autoAlpha: 0, y: -22, scale: 0.92, duration: 0.5, ease: 'power2.in' },
        t + 0.55
      );
    });

    // PHASE 3 — LOCK (3.05 → 3.6s). Counter scale-pops at 62, "✓ 4.9 RATING"
    // pill slides out below the caption — same lock-in pattern as 1.1's FAST,
    // 1.2's REPLIED/BOOKED, 1.3's BOOKED BACK.
    tl.fromTo('.rev-counter-num',
      { scale: 1 },
      { scale: 1.18, duration: 0.18, ease: 'power2.out', yoyo: true, repeat: 1 },
      3.05
    );
    tl.fromTo('.rev-pill',
      { autoAlpha: 0, y: -4, scale: 0.8, xPercent: -50 },
      { autoAlpha: 1, y: 0, scale: 1, xPercent: -50, duration: 0.4, ease: 'back.out(1.8)' },
      3.2
    );

    // PHASE 4 — RESOLVE (4.4 → 5.5s). Pill fades, hold, loop.
    tl.to('.rev-pill', { autoAlpha: 0, duration: 0.3, ease: 'power2.in' }, 4.4);
    tl.to({}, { duration: 0.6 }, 5.0);

    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => { if (e.isIntersecting) tl.play(); else tl.pause(); });
    }, { threshold: 0.2 });
    io.observe(root);

    return () => {
      io.disconnect();
      tl.kill();
    };
  }, []);

  return (
    <div className="pc-visual-card vis-reviews" ref={rootRef}>
      <div className="pc-visual-meta">Review prompt · post-visit</div>
      <div className="pc-visual-stage">
        {/* Hero — 5 stars + big counter + caption + lock pill, all centered */}
        <div className="rev-hero">
          <div className="rev-stars">
            <span className="rev-star">★</span>
            <span className="rev-star">★</span>
            <span className="rev-star">★</span>
            <span className="rev-star">★</span>
            <span className="rev-star">★</span>
          </div>
          <strong className="rev-counter-num">14</strong>
          <div className="rev-counter-caption">Google reviews · +48 in 8 weeks</div>
          <div className="rev-pill-slot">
            <div className="rev-pill">✓ 4.9 RATING</div>
          </div>
        </div>

        {/* Floating +1 ★ pulses — incoming reviews popping during the climb */}
        <div className="rev-pulse rev-pulse-1" aria-hidden="true">+1 ★</div>
        <div className="rev-pulse rev-pulse-2" aria-hidden="true">+1 ★</div>
        <div className="rev-pulse rev-pulse-3" aria-hidden="true">+1 ★</div>

        {/* Pocky bottom-right — canonical brand-pack PNG, same bob as 1.1-1.3 */}
        <div className="vis-pocky-anchor pos-br rev-pocky-anchor">
          <img src="assets/pocky-working.png" alt="" className="sw-pocky-img sw-pocky-corner" />
        </div>
      </div>
    </div>
  );
};

const LayerVisual = ({ kind, alt }) => {
  /* Layer 1 cards: smart-website mockup, missed-call SMS routing, dormant-customer
     reactivation grid, review feed. Layer 2 cards: tag-flow visual with animated Pocky. */
  if (kind === 'site') {
    return <SmartWebsiteVisual />;
  }
  if (kind === 'callback') {
    return <MissedCallVisual />;
  }
  if (kind === 'reactivate') {
    return <ReactivateVisual />;
  }
  if (kind === 'reviews') {
    return <ReviewsVisual />;
  }
  /* Layer 2 fallback — keep the existing tag-flow visual but with animated SVG Pocky */
  const M = {
    hair: { meta: 'Slot-fill at peak', a: 'Sat 14:00 booked', b: 'Sat 16:00 open', c: '+1 walk-in slotted' },
    medspa: { meta: 'Touch-Up reminder', a: 'Botox · 3mo', b: 'Hyaluron · 6mo', c: 'Deposit ✓' },
    gym: { meta: 'Retention trigger', a: '−3 visits', b: 'Trigger fired', c: 'Class booked' },
    pet: { meta: 'Grooming-cycle', a: 'Mochi · 28d', b: 'Booking sent', c: 'Confirmed' },
  }[kind] || {};
  return (
    <div className="pc-visual-card">
      <div className="pc-visual-meta">{M.meta}</div>
      <div className="pc-visual-stage">
        <div className="vis-pocky-anchor pos-center">
          <PockyAnimated size={76} mood="work" />
        </div>
        <div className="pc-visual-tag t-a">{M.a}</div>
        <div className="pc-visual-tag t-b">{M.b}</div>
        <div className="pc-visual-tag t-c">{M.c}</div>
        <svg className="pc-visual-paths" viewBox="0 0 100 60" preserveAspectRatio="none">
          <path d="M 18 18 Q 40 18 50 30" />
          <path d="M 18 30 Q 35 30 50 30" />
          <path d="M 50 30 Q 70 42 82 42" />
        </svg>
      </div>
    </div>
  );
};

const SolutionSection = () => {
  const [layer, setLayer] = useState(1);
  const [activeCard, setActiveCard] = useState(0);
  const data = LAYER_DATA[layer];
  const card = data.cards[activeCard];

  const switchLayer = (n) => { setLayer(n); setActiveCard(0); };

  return (
    <section className="pc-section surface" id="solution">
      <div className="pc-container">
        <div className="pc-section-head">
          <Eyebrow center>§ 03 · The infrastructure we build</Eyebrow>
          <h2 className="pc-display-lg">Digital infrastructure + workforce. Built end-to-end.</h2>
          <p className="pc-lede">Two layers. Foundation captures every customer. Workforce takes the work off your desk.</p>
        </div>

        {/* Two-layer stack — one is active, one is dimmed */}
        <div className="pc-stack-toggle">
          <button
            className={`pc-stack-slab v2 lower ${layer === 1 ? 'is-active' : 'is-dim'}`}
            onClick={() => switchLayer(1)}
            aria-pressed={layer === 1}
          >
            <div className="layer-label">Layer 1</div>
            <div className="layer-body">
              <div className="layer-name">Digital Infrastructure</div>
              <div className="layer-sub">Foundation. Universal. Built once.</div>
            </div>
            <div className="layer-arrow">{layer === 1 ? '↓' : '→'}</div>
          </button>
          <button
            className={`pc-stack-slab v2 upper ${layer === 2 ? 'is-active' : 'is-dim'}`}
            onClick={() => switchLayer(2)}
            aria-pressed={layer === 2}
          >
            <div className="layer-label">Layer 2</div>
            <div className="layer-body">
              <div className="layer-name">Digital Workforce</div>
              <div className="layer-sub">Niche-specific employees, integrated.</div>
            </div>
            <div className="layer-arrow">{layer === 2 ? '↓' : '→'}</div>
          </button>
        </div>

        {/* Active layer detail */}
        <div className="pc-layer-detail" key={layer}>
          <div className="pc-layer-tabs">
            {data.cards.map((c, i) => (
              <button
                key={c.id}
                className={`pc-layer-tab ${i === activeCard ? 'is-active' : ''}`}
                onClick={() => setActiveCard(i)}
              >
                {c.label}
              </button>
            ))}
          </div>

          <div className={`pc-layer-card ${card.image ? 'has-pocky-scene' : ''}`} key={card.id}>
            <div className="pc-layer-card-text">
              <Eyebrow>{card.role || card.label}</Eyebrow>
              <h3>{card.h}</h3>
              {card.sub ? <p className="pc-layer-sub">{card.sub}</p> : null}
              {card.oneLine ? <p className="pc-layer-oneline">{card.oneLine}</p> : null}
              {card.features ? (
                <>
                  <div className="pc-layer-features" data-snap-rail={`layer-feat-${card.id}`}>
                    {card.features.map((f, i) => (
                      <div className="pc-feature" key={i}>
                        <FeatureIcon iconKey={f.iconKey} />
                        <h4 className="pc-feature-title">{f.title}</h4>
                        <p className="pc-feature-desc">{f.desc}</p>
                      </div>
                    ))}
                  </div>
                  <div className="pc-snap-dots pc-layer-feat-dots" data-snap-dots={`layer-feat-${card.id}`} role="tablist" aria-label={`${card.role || card.label || 'Features'} navigation`}>
                    {card.features.map((f, i) => (
                      <button
                        key={i}
                        type="button"
                        role="tab"
                        aria-selected={i === 0 ? 'true' : 'false'}
                        className={i === 0 ? 'is-active' : ''}
                        aria-label={`Feature ${i + 1} of ${card.features.length}: ${f.title}`}
                      ></button>
                    ))}
                  </div>
                </>
              ) : card.bullets ? (
                <ul className="pc-layer-bullets">
                  {card.bullets.map((b, i) => <li key={i}>{b}</li>)}
                </ul>
              ) : null}
              {card.p ? <p>{card.p}</p> : null}
              <div className="pc-layer-kpi">{card.kpi}</div>
              {card.cta ? (
                <a className="pc-btn pc-btn-ghost pc-btn-sm pc-layer-cta" href="#hero-form" onClick={(e) => { e.preventDefault(); scrollToId('hero-form'); }}>{card.cta}</a>
              ) : null}
            </div>
            {card.image ? (
              <div className="pc-pocky-scene">
                <img src={card.image} alt={card.imageAlt} loading="lazy" width="1024" height="1024" />
              </div>
            ) : (
              <LayerVisual kind={card.visual} alt={card.imageAlt} />
            )}
          </div>
        </div>
      </div>
    </section>
  );
};

/* ---------- §04 LEAD MAGNET · audit form (moved out of hero) ---------- */
const LeadMagnet = () => {
  useEffect(() => {
    if (!document.querySelector('script[data-ghl-embed]')) {
      const s = document.createElement('script');
      s.src = 'https://gg.gopocketceo.com/js/form_embed.js';
      s.async = true;
      s.setAttribute('data-ghl-embed', 'true');
      document.body.appendChild(s);
    }
  }, []);

  return (
    <section className="pc-leadmagnet" id="audit">
      <div className="pc-leadmagnet-glow"></div>
      <div className="pc-container pc-leadmagnet-grid">
        {/* LEFT — pitch + Pocky greeting */}
        <div className="pc-leadmagnet-pitch">
          <Eyebrow>§ 04 · The audit</Eyebrow>
          <h2 className="pc-display-lg">Get a 5-min audit of your business.</h2>
          <p className="pc-lede">Tell us your biggest leak. Within 24 hours we send a private Loom showing where you're losing customers and how we'd fix it.</p>
          <p className="pc-form-trust">HK BR 78290657 · Sheung Wan · 24-hour reply · No call required to start</p>
          <div className="pc-leadmagnet-pocky" aria-hidden="true">
            <img src="assets/pocky-gift.png" alt="" />
          </div>
        </div>

        {/* RIGHT — GHL embedded form */}
        <div className="pc-hero-form-card pc-ghl-form" id="hero-form">
          <iframe
            src="https://gg.gopocketceo.com/widget/form/Jl7PrwgJG4LChYwsEKB7"
            style={{ width: '100%', minHeight: 540, border: 'none', borderRadius: 8, display: 'block' }}
            id="inline-Jl7PrwgJG4LChYwsEKB7"
            data-layout={"{'id':'INLINE'}"}
            data-trigger-type="alwaysShow"
            data-trigger-value=""
            data-activation-type="alwaysActivated"
            data-activation-value=""
            data-deactivation-type="neverDeactivate"
            data-deactivation-value=""
            data-form-name="Pocket CEO"
            data-height="501"
            data-layout-iframe-id="inline-Jl7PrwgJG4LChYwsEKB7"
            data-form-id="Jl7PrwgJG4LChYwsEKB7"
            title="Pocket CEO"
          ></iframe>
        </div>
      </div>
    </section>
  );
};

/* ---------- §05 ABOUT MINI · slim elevator pitch + founder portraits ---------- */
const AboutMini = () => (
  <section className="pc-about-mini" id="about">
    <div className="pc-container">
      <div className="pc-about-mini-strip">
        <p className="pc-about-mini-pitch">
          Pocket CEO is the Hong Kong AI agency turning service-business owners back into CEOs via AI infrastructure and a digital workforce.
        </p>
        <div className="pc-about-mini-founders">
          <a className="pc-founder" href="https://www.linkedin.com/in/varunrazdan/" target="_blank" rel="noopener noreferrer">
            <img src="assets/founder-varun.webp" alt="Varun Razdan" />
            <span><strong>Varun Razdan</strong> Hong Kong · Tigerpaw author</span>
          </a>
          <span className="pc-founder-sep" aria-hidden="true">·</span>
          <a className="pc-founder" href="https://www.linkedin.com/in/luka-knieling/" target="_blank" rel="noopener noreferrer">
            <img src="assets/founder-luka.webp" alt="Luka Knieling" />
            <span><strong>Luka Knieling</strong> Munich · n8n + integrations</span>
          </a>
        </div>
        <a href="https://gopocketceo.com" className="pc-about-mini-cta">
          More about Pocket CEO
          <span className="pc-about-mini-cta-arrow">→</span>
        </a>
      </div>
    </div>
  </section>
);

/* ---------- §06 CALENDAR · GoHighLevel iframe (mirror Main LP /kontakt) ---------- */
const Calendar = () => (
  <div className="calendar-iframe-wrap">
    <iframe
      className="calendar-iframe"
      data-src="https://gg.gopocketceo.com/widget/booking/M4nlXwejTXPPhJEq2tUW"
      src="about:blank"
      scrolling="no"
      id="TYto9s49o0i8mxfN40M2_1777358286993"
      title="Book a 40-min call with Pocket CEO"
      loading="lazy"
    ></iframe>
    <div className="calendar-iframe-skeleton" aria-hidden="true">
      <span className="calendar-skeleton-text">Loading calendar…</span>
    </div>
  </div>
);

const FinalSection = () => {
  const ref = useRef(null);
  const [lit, setLit] = useState([false, false, false, false]);
  useEffect(() => {
    const obs = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          [0,1,2,3].forEach((i) => setTimeout(() => setLit((prev) => { const n = [...prev]; n[i] = true; return n; }), 200 + i * 220));
          obs.disconnect();
        }
      });
    }, { threshold: 0.25 });
    if (ref.current) obs.observe(ref.current);
    return () => obs.disconnect();
  }, []);

  const steps = [
    ['Wk 1 · Discovery + audit', 'We look live at your Google, OpenRice, WhatsApp, existing site.'],
    ['Wk 2-4 · Build foundation', 'Smart website, missed-call text-back, reactivation, reviews go live.'],
    ['Wk 5-6 · Hire workforce', 'Niche-specific digital employees integrated on top of the foundation.'],
    ['Wk 6+ · Retainer', 'Ongoing tuning. We watch the numbers, you watch the business.'],
  ];

  return (
    <section className="pc-section pc-final" id="booking" ref={ref}>
      <div className="pc-container">
        <div className="pc-section-head">
          <Eyebrow dark center>§ 05 · Book the call</Eyebrow>
          <h2 className="pc-display-lg">40 minutes. Real diagnosis. No pitch deck.</h2>
          <p className="pc-lede">We look live at your Google profile, OpenRice, WhatsApp, and existing site. You leave knowing where revenue leaks and what fixing it costs.</p>
          <p className="pc-final-trust"><span className="pc-final-trust-pill">DTSPP-eligible</span> Up to HK$50,000 government subsidy for F&amp;B, retail, tourism, and personal-services SMBs in Hong Kong.</p>
        </div>
        <div className="pc-final-grid">
          <div className="pc-process-vert">
            {steps.map(([meta, body], i) => (
              <div key={meta} className={`step ${lit[i] ? 'lit' : ''}`}>
                <div className="step-meta">{meta}</div>
                <div className="step-body">{body}</div>
              </div>
            ))}
          </div>
          <Calendar />
        </div>
      </div>
      <div className="pc-final-pocky" aria-hidden="true">
        <img src="assets/pocky-walking.png" alt="" />
      </div>
    </section>
  );
};

/* ---------- §06 FAQ ---------- */
const FAQ_ITEMS = [
  ['Do you really do 繁中 customer flows?', 'Real Cantonese-speaker reviewed before launch. We calibrate the tone on your existing customer messages so the voice matches what regulars expect. Not template Mandarin, not auto-translation.'],
  ['Which booking system do I keep or replace?', 'If your booking tool works (Treatwell, Booksy, in-house, carrier portal, Excel), we keep it and integrate. If you don\'t have one, we build a clean one inside the smart website. No proprietary platform to migrate to.'],
  ['What\'s the difference between Layer 1 and Layer 2?', 'Layer 1 is the digital infrastructure every service business needs · smart website, missed-call text-back + chatbot, database reactivation, reviews. Layer 2 is the digital workforce: niche-specific digital employees we hire into your operation, diagnosed per business in the audit. Most need both. Some only want Layer 1.'],
  ['What about PCPD compliance?', 'Customer data processed under the PCPD AI Model Framework with audit trails on every automated message. No review-gating (PCPD-safe). EU + HK servers. Owner signs off on every flagged action.'],
  ['How long does setup take?', 'Layer 1 foundation goes live in roughly 4 weeks. Layer 2 custom automation layers in over the next 2 to 4 weeks depending on scope. Ongoing tuning happens in the monthly retainer.'],
];

const FAQ_JSONLD = JSON.stringify({
  '@context': 'https://schema.org',
  '@type': 'FAQPage',
  mainEntity: FAQ_ITEMS.map(([q, a]) => ({
    '@type': 'Question',
    name: q,
    acceptedAnswer: { '@type': 'Answer', text: a },
  })),
});

const FAQ = () => {
  const [open, setOpen] = useState(0);
  return (
    <section className="pc-section" id="faq">
      <div className="pc-container">
        <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: FAQ_JSONLD }} />
        <div className="pc-section-head">
          <Eyebrow center>§ 06 · Questions</Eyebrow>
          <h2 className="pc-display-lg">What HK service-business owners ask.</h2>
        </div>
        <div className="pc-faq-list">
          {FAQ_ITEMS.map(([q, a], i) => (
            <div key={i} className={`pc-faq-item ${open === i ? 'open' : ''}`}>
              <button className="pc-faq-q" onClick={() => setOpen(open === i ? -1 : i)}>
                <span>{q}</span>
                <span className="marker">+</span>
              </button>
              <div className="pc-faq-a">
                <div className="pc-faq-a-inner">{a}</div>
              </div>
            </div>
          ))}
        </div>
        <div className="pc-faq-foot">
          <a href="https://gopocketceo.com/#faq">More questions? See the main FAQ →</a>
        </div>
      </div>
    </section>
  );
};

/* ---------- FOOTER ---------- */
const Footer = () => (
  <footer className="pc-footer">
    <div className="pc-container pc-footer-inner">
      <div className="pc-footer-brand">
        <img src="assets/pocket-ceo-lockup-dark.png" alt="Pocket CEO" width="2560" height="416" />
        <p>The AI agency that turns SMB owners back into CEOs via AI infrastructure + digital workforce.</p>
      </div>
      <div className="pc-footer-cols">
        <div>
          <span className="pc-footer-h">Pocket CEO</span>
          <a href="https://gopocketceo.com">Main site</a>
          <a href="https://gopocketceo.com/insurance/">Insurance · HK</a>
        </div>
        <div>
          <span className="pc-footer-h">Legal entity</span>
          <span>Pocket CEO Limited</span>
          <span>HK BR 78290657</span>
          <span>Sheung Wan, Tower 1 Tern Centre</span>
        </div>
        <div>
          <span className="pc-footer-h">Connect & legal</span>
          <a href="https://www.linkedin.com/in/varunrazdan1" target="_blank" rel="noopener">LinkedIn · Varun</a>
          <a href="https://www.linkedin.com/in/luka-knieling/" target="_blank" rel="noopener">LinkedIn · Luka</a>
          <a href="https://gopocketceo.com/privacy.html">Privacy</a>
          <a href="https://gopocketceo.com/impressum.html">Impressum</a>
          <a href="https://gopocketceo.com/terms.html">Terms</a>
        </div>
      </div>
    </div>
    <div className="pc-footer-disclaim">
      Pocket CEO is a software and automation provider. Customer data is processed under the PCPD AI Model Framework with audit trails on every automated message.
    </div>
    <div className="pc-footer-base">
      <span>© 2026 Pocket CEO Limited</span>
      <span>HK BR 78290657 · Sheung Wan</span>
    </div>
  </footer>
);

Object.assign(window, { Nav, Hero, PainSection, SolutionSection, LeadMagnet, AboutMini, FinalSection, FAQ, Footer, LayerVisual, SmartWebsiteVisual, MissedCallVisual, ReactivateVisual, ReviewsVisual, LAYER_DATA, PockyAnimated });
