/* Golden Harmonic Memorial Park — main site */
const { useState, useEffect, useMemo, useRef } = React;

/* Locking body scroll with plain `overflow:hidden` doesn't reliably stop touch-scroll
   on mobile Safari/Chrome and can cause the page to jump — pinning body to `position:
   fixed` at its current scroll offset (then restoring it on close) is the technique that
   actually works across mobile browsers. Shared by the Lightbox and the mobile nav drawer. */
function useBodyScrollLock(active) {
  useEffect(() => {
    if (!active) return;
    const scrollY = window.scrollY;
    const body = document.body;
    const prev = { position: body.style.position, top: body.style.top, left: body.style.left, right: body.style.right, width: body.style.width };
    body.style.position = 'fixed';
    body.style.top = `-${scrollY}px`;
    body.style.left = '0';
    body.style.right = '0';
    body.style.width = '100%';
    return () => {
      body.style.position = prev.position;
      body.style.top = prev.top;
      body.style.left = prev.left;
      body.style.right = prev.right;
      body.style.width = prev.width;
      window.scrollTo(0, scrollY);
    };
  }, [active]);
}

/* ---------- Palettes ----------
   Keyed by the swatch array so TweakColor can pass the array directly back. */
const PALETTE_OPTIONS = [
['#2f5d4c', '#f6f1e8', '#b08544'], // Sage & Sand
['#2a4570', '#f5efe6', '#c9913f'], // River Dawn
['#73302f', '#f4ecdf', '#a8843a'] // Garden Heritage
];

const PALETTES = {
  '#2f5d4c': {
    '--bg': '#f6f1e8', '--bg-2': '#ece4d3', '--ink': '#1f2a26', '--ink-2': '#4a564f',
    '--line': '#d9cfbb', '--accent': '#2f5d4c', '--accent-ink': '#f6f1e8',
    '--gold': '#b08544', '--terracotta': '#b8593a', '--card': '#fbf7ee'
  },
  '#2a4570': {
    '--bg': '#f5efe6', '--bg-2': '#e8dfd0', '--ink': '#1c2535', '--ink-2': '#475064',
    '--line': '#d4c9b7', '--accent': '#2a4570', '--accent-ink': '#f5efe6',
    '--gold': '#c9913f', '--terracotta': '#c25a3f', '--card': '#fbf6ed'
  },
  '#73302f': {
    '--bg': '#f4ecdf', '--bg-2': '#e7dcc7', '--ink': '#2a1a1a', '--ink-2': '#574141',
    '--line': '#d2c3a8', '--accent': '#73302f', '--accent-ink': '#f4ecdf',
    '--gold': '#a8843a', '--terracotta': '#9d4a32', '--card': '#faf3e5'
  }
};

/* ---------- Pricing (base in PHP) ----------
   Sourced from the park's actual price list. No down payment on any plan —
   installment plans instead apply a surcharge to the spot-cash price, then
   divide evenly by the term. Verified against every example in the source
   price list (Regular, Garden, and Family Vault categories all matched this
   exact schedule): */
const PLAN_TERMS = [
{ key: 'cash', label: 'Spot Cash', months: 0, surcharge: 0 },
{ key: '1yr', label: '1-Year Plan', months: 12, surcharge: 0.10 },
{ key: '2yr', label: '2-Year Plan', months: 24, surcharge: 0.15 },
{ key: '3yr', label: '3-Year Plan', months: 36, surcharge: 0.20 },
{ key: '5yr', label: '5-Year Plan', months: 60, surcharge: 0.30 }];


const BASE_TIERS = [
{
  id: 'regular', name: 'Regular Plot', category: 'Regular Plots', price: 30000,
  desc: 'A standard single-interment lot in our open-lawn sections. Priced per crypt/share.',
  features: ['1 interment space', 'Perpetual care included', 'Open-lawn setting'],
  label: 'regular plot — open lawn', image: '/regular_plot.jpg'
},
{
  id: 'premium', name: 'Premium Plot', category: 'Regular Plots', price: 42500,
  desc: 'An upgraded standard plot in one of the park\'s preferred sections. Priced per crypt/share.',
  features: ['1 interment space', 'Perpetual care included', 'Preferred section placement'],
  label: 'premium plot — preferred section', image: '/premium_plot.jpg'
},
{
  id: 'corner-premium', name: 'Corner Premium Plot', category: 'Regular Plots', price: 47500,
  desc: 'A corner plot in our most requested standard section. Priced per crypt/share.',
  features: ['1 interment space', 'Perpetual care included', 'Corner placement'],
  label: 'corner premium plot', image: '/corner_premium_plot.jpg'
},
{
  id: 'garden-regular', name: 'Regular Garden Plot', category: 'Garden Plots', price: 37500,
  desc: 'A single-interment lot within our landscaped garden sections. Priced per crypt/share.',
  features: ['1 interment space', 'Perpetual care included', 'Garden setting'],
  label: 'regular garden plot'
},
{
  id: 'garden-premium', name: 'Premium Garden Plot', category: 'Garden Plots', price: 47500,
  desc: 'An upgraded garden plot in a preferred section of the garden. Priced per crypt/share.',
  features: ['1 interment space', 'Perpetual care included', 'Preferred garden section'],
  label: 'premium garden plot'
},
{
  id: 'garden-corner', name: 'Corner Prime Garden Plot', category: 'Garden Plots', price: 57500,
  desc: 'A corner plot in our most requested garden section. Priced per crypt/share.',
  features: ['1 interment space', 'Perpetual care included', 'Prime corner placement'],
  label: 'corner prime garden plot'
},
{
  id: 'family-vault', name: 'Family Vault Package', category: 'Family Vault', price: 105000,
  desc: 'An apartment-style below-ground vault holding 3 coffins in one plot.',
  features: ['3 coffins in one plot', 'Apartment-style below-ground vault', 'Payment plans available (surcharge applies)'],
  label: 'family vault — apartment-style'
},
{
  // Live price comes from Firestore 'products' (id: 'valor'); this is the fallback
  id: 'valor', name: 'Valor Plot', category: 'Family Vault', price: 40000,
  desc: 'A dignified resting place honoring those who served — veterans and service members.',
  features: ['2 interment spaces', 'Perpetual care included', 'Memorial marker with laurel emblem'],
  label: 'valor plot — memorial marker', image: '/valor_plot.png'
}];const PLOT_MESSAGES = {
  'regular': 'A peaceful, standard resting place in our open-lawn section — a simple and dignified choice for your loved one.',
  'premium': 'A preferred-section resting place offering a quieter, more intimate setting within the park.',
  'corner-premium': 'A corner placement in our most requested section — extra space and a distinguished location.',
  'garden-regular': 'A resting place surrounded by landscaped gardens, offering a serene and naturally beautiful setting.',
  'garden-premium': 'An upgraded garden placement in one of our most sought-after sections, combining beauty and tranquility.',
  'garden-corner': 'Our most exclusive garden placement — a prime corner spot in the most requested section of the park.',
  'family-vault': 'A shared resting place for up to three loved ones in one apartment-style vault — ideal for keeping family together.',
  'valor': 'A dignified resting place honoring those who served — a fitting tribute to a veteran or service member.'
}


// Mausoleum: land and construction are priced as ranges (they depend on the
// exact size and design), so these live outside the fixed-price calculator
// above and are presented as request-a-quote items instead.
const MAUSOLEUM_PACKAGES = [
{ size: 'Serenity Glass Mausoleum', area: '12–20 sqm', low: 500000, high: 700000, desc: 'A modern and elegant design for a peaceful remembrance.', image: '/mausoleum_serenity.jpg' },
{ size: 'Elysian Mausoleum', area: '30–50 sqm', low: 1200000, high: 1800000, desc: 'A timeless sanctuary of faith, love and remembrance.', image: '/mausoleum_elysian.jpg' },
{ size: 'Solace Mausoleum', area: '70–100+ sqm', low: 3000000, high: null, desc: 'A serene and intimate space for your loved ones.', image: '/mausoleum_solace.jpg' }];


const TWO_STORY_MAUSOLEUM = {
  area: '120–160 sqm', low: 5000000, high: 7000000,
  desc: 'Two floors: a ground-floor memorial chamber, comfort room, and reception area, plus a second-floor family rest area, kitchenette, and reflection nook.'
};


const fmt = (n) => '₱' + Math.round(n).toLocaleString('en-PH');

/* ---------- Plan selection (shared between tier cards and the calculator) ---------- */
const PlanSelectionContext = React.createContext({ selectedTierId: null, selectTier: () => {} });
function usePlanSelection() { return React.useContext(PlanSelectionContext); }

/* ---------- Cart (shared between Tier cards, Header, and the Cart Drawer) ---------- */
const CART_STORAGE_KEY = 'gh_cart_v1';
const CartContext = React.createContext({
  cart: [], addToCart: () => {}, removeFromCart: () => {}, updateQty: () => {}, clearCart: () => {},
  cartCount: 0, cartOpen: false, setCartOpen: () => {}
});
function useCart() { return React.useContext(CartContext); }

/* ---------- Lightbox (shared across Gallery, Tiers) ---------- */
const LightboxContext = React.createContext(() => {});
function useLightbox() { return React.useContext(LightboxContext); }

function LightboxRoot({ children }) {
  const [state, setState] = useState(null); // { items: [{label, caption}], index }

  const open = (items, index = 0) => setState({ items, index });
  const close = () => setState(null);
  const step = (dir) => setState((s) => s && { ...s, index: (s.index + dir + s.items.length) % s.items.length });

  useBodyScrollLock(!!state);

  useEffect(() => {
    if (!state) return;
    const onKey = (e) => {
      if (e.key === 'Escape') close();
      if (e.key === 'ArrowRight') step(1);
      if (e.key === 'ArrowLeft') step(-1);
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [state]);

  return (
    <LightboxContext.Provider value={open}>
      {children}
      {state &&
      <div className="lightbox-overlay" onClick={close}>
          <div className="lightbox-inner" onClick={(e) => e.stopPropagation()}>
            <button className="lb-btn lb-close" onClick={close} aria-label="Close">✕</button>
            {state.items.length > 1 &&
          <button className="lb-btn lb-prev" onClick={() => step(-1)} aria-label="Previous">‹</button>}

            <div className={`ph aspect-wide ${(state.items[state.index].image || state.items[state.index].scene) ? 'illustrated' : ''}`}>
              {state.items[state.index].image
                ? <img
                    src={state.items[state.index].image}
                    alt={state.items[state.index].label}
                    className="ph-image"
                    style={state.items[state.index].imagePosition ? { objectPosition: state.items[state.index].imagePosition } : undefined} />
                : state.items[state.index].scene
                ? <Scene variant={state.items[state.index].scene} />
                : <div className="ph-label">{state.items[state.index].label}</div>}
            </div>
            {state.items.length > 1 &&
          <button className="lb-btn lb-next" onClick={() => step(1)} aria-label="Next">›</button>}

            <div className="lightbox-caption">
              <span>{state.items[state.index].caption}</span>
              {state.items.length > 1 &&
            <span className="lb-count">{state.index + 1} / {state.items.length}</span>}

            </div>
          </div>
        </div>}

    </LightboxContext.Provider>);

}

/* Clickable photo frame — drop-in replacement for a bare .ph placeholder */
function Photo({ label, caption, items, index, aspect = '', className = '', style, scene, image, imagePosition }) {
  const openLightbox = useLightbox();
  const group = (items || [{ label, caption: caption || label }]).map((it) => ({
    image: it.image || image,
    scene: it.scene || scene,
    imagePosition: it.imagePosition || imagePosition,
    label: it.label,
    caption: it.caption
  }));
  const idx = index || 0;
  const isArt = scene || image;
  return (
    <div
      className={`ph ${aspect} clickable ${isArt ? 'illustrated' : ''} ${className}`}
      style={style}
      onClick={() => openLightbox(group, idx)}
      role="button"
      tabIndex={0}
      onKeyDown={(e) => { if (e.key === 'Enter') openLightbox(group, idx); }}>

      {image
        ? <img src={image} alt={label} className="ph-image" style={imagePosition ? { objectPosition: imagePosition } : undefined} />
        : scene ? <Scene variant={scene} /> : <div className="ph-label">{label}</div>}
      {isArt && <div className="ph-label ph-label-overlay">{label}</div>}
      <div className="ph-expand">⤢</div>
    </div>);

}

/* ---------- Illustrated scenes (stand-in artwork until real photos are ready) ---------- */
const FOLIAGE = '#7c8a63';
const FOLIAGE_DARK = '#5e6c49';
const WATER = '#7f97a3';

function hillsPath(seed, amplitude, baseY) {
  let d = `M0,300 L0,${baseY.toFixed(1)}`;
  for (let i = 0; i <= 12; i++) {
    const x = (i / 12) * 400;
    const y = baseY - amplitude * Math.sin((i + seed) * 0.9) - amplitude * 0.4 * Math.sin((i + seed) * 2.3);
    d += ` L${x.toFixed(1)},${y.toFixed(1)}`;
  }
  return d + ' L400,300 Z';
}

function TreeCluster({ x, y, scale = 1, color }) {
  return (
    <g transform={`translate(${x},${y}) scale(${scale})`}>
      <circle cx="-9" cy="1" r="9" fill={color} opacity="0.9" />
      <circle cx="6" cy="-5" r="12" fill={color} />
      <circle cx="15" cy="2" r="7" fill={color} opacity="0.88" />
    </g>);

}

function ChapelSilhouette({ x, y, scale = 1, color }) {
  return (
    <g transform={`translate(${x},${y}) scale(${scale})`}>
      <path d="M-22,2 L-22,-28 L0,-46 L22,-28 L22,2 Z" fill={color} />
      <rect x="-4" y="-64" width="8" height="20" fill={color} />
      <rect x="-2" y="-72" width="4" height="9" fill={color} />
      <rect x="-5" y="-10" width="10" height="12" fill="var(--card)" opacity="0.55" />
    </g>);

}

function Monument({ x, y, scale = 1, color }) {
  return (
    <g transform={`translate(${x},${y}) scale(${scale})`}>
      <rect x="-6" y="-38" width="12" height="38" fill={color} />
      <path d="M-6,-38 L0,-50 L6,-38 Z" fill={color} />
    </g>);

}

function MarkerRow({ y, count, color, spacing = 44 }) {
  const start = (400 - count * spacing) / 2 + spacing / 2 - 9;
  return (
    <>
      {Array.from({ length: count }).map((_, i) =>
      <rect key={i} x={start + i * spacing} y={y} width="18" height="9" rx="2" fill={color} opacity="0.85" />
      )}
    </>);

}

function WaterBand({ y, height }) {
  return (
    <g>
      <rect x="0" y={y} width="400" height={height} fill={WATER} opacity="0.55" />
      {[0, 1, 2, 3].map((i) =>
      <rect key={i} x={16 + i * 95} y={y + height * 0.35 + i * 2.5} width="58" height="3" rx="2" fill="#ffffff" opacity="0.22" />
      )}
    </g>);

}

/* ---------- Hero signature: the soul's passage ----------
   Palawan's Tabon Caves hold the oldest funerary art in the Philippines —
   carved boats guiding the soul onward, the same motif later immortalized
   in the Manunggul jar. This hero scene draws on that lineage directly:
   karst cliffs at dusk, a wooden outrigger under a rising moon, and the
   bioluminescent plankton that light Palawan's bays after dark. */
function HeroVoyageScene() {
  return (
    <svg className="scene-svg" viewBox="0 0 400 500" preserveAspectRatio="xMidYMid slice" aria-hidden="true">
      <defs>
        <linearGradient id="voyage-sky" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor="#1c2e2a" />
          <stop offset="55%" stopColor="#5c5236" />
          <stop offset="100%" stopColor="#caa15a" />
        </linearGradient>
        <linearGradient id="voyage-water" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor="#8a7548" />
          <stop offset="35%" stopColor="#4d564c" />
          <stop offset="100%" stopColor="#182420" />
        </linearGradient>
      </defs>

      <rect width="400" height="340" fill="url(#voyage-sky)" />
      <circle cx="300" cy="80" r="50" fill="#f7ecd1" opacity="0.12" />
      <circle cx="300" cy="80" r="24" fill="#f7ecd1" opacity="0.9" />

      {/* distant karst, then nearer karst — Palawan's limestone cliffline, not rolling hills */}
      <path d="M0,230 L0,210 L25,180 L45,205 L70,150 L95,195 L120,140 L150,190 L175,120 L205,185 L230,145 L260,195 L290,130 L320,190 L350,150 L375,200 L400,170 L400,340 L0,340 Z"
        fill="var(--accent)" opacity="0.22" />
      <path d="M0,260 L20,230 L40,250 L65,190 L85,240 L110,170 L140,235 L165,150 L195,225 L220,160 L250,230 L275,180 L305,220 L330,175 L360,230 L385,195 L400,215 L400,340 L0,340 Z"
        fill="var(--ink)" opacity="0.55" />

      <rect x="0" y="330" width="400" height="170" fill="url(#voyage-water)" />
      <ellipse cx="300" cy="345" rx="66" ry="2.5" fill="#f6dfa8" opacity="0.3" />
      <ellipse cx="292" cy="360" rx="80" ry="3" fill="#f6dfa8" opacity="0.2" />
      <ellipse cx="280" cy="380" rx="96" ry="3.5" fill="#f6dfa8" opacity="0.12" />

      <g className="voyage-specks">
        {[[64,368,2.2,.6],[92,392,1.7,.8],[118,410,2.4,.5],[150,430,2,.9],[175,405,2.2,.6],
          [205,450,1.8,.7],[230,420,2.3,.5],[255,465,1.9,.8],[280,440,2.2,.6],[310,470,1.8,.7],
          [335,415,2.4,.5],[105,445,1.7,.7],[190,460,2,.6]].map(([cx,cy,r,op], i) =>
          <circle key={i} cx={cx} cy={cy} r={r} fill="#8fe3c9" opacity={op}
            style={{ animationDelay: `${(i % 5) * 0.6}s` }} />
        )}
      </g>

      <g className="voyage-boat">
        <ellipse cx="110" cy="382" rx="18" ry="3" fill="none" stroke="#f6f1e8" strokeWidth="1" opacity="0.22" />
        <ellipse cx="94" cy="387" rx="27" ry="3" fill="none" stroke="#f6f1e8" strokeWidth="1" opacity="0.13" />
        <path d="M120,378 C150,368 230,368 260,378 C245,388 215,392 190,392 C165,392 135,388 120,378 Z" fill="var(--ink)" />
        <path d="M258,378 C270,368 278,352 274,338 C266,352 258,364 250,373 Z" fill="var(--ink)" />
        <path d="M122,378 C112,370 106,358 108,348 C114,358 120,368 128,374 Z" fill="var(--ink)" />
        <ellipse cx="225" cy="362" rx="6" ry="14" fill="var(--ink)" />
        <circle cx="225" cy="345" r="5" fill="var(--ink)" />
        <line x1="232" y1="355" x2="246" y2="391" stroke="var(--ink)" strokeWidth="2" strokeLinecap="round" />
        <ellipse cx="165" cy="364" rx="6" ry="13" fill="var(--ink)" />
        <circle cx="165" cy="348" r="5" fill="var(--ink)" />
        <line x1="158" y1="358" x2="140" y2="392" stroke="var(--ink)" strokeWidth="2" strokeLinecap="round" />
      </g>

      <rect x="0" y="470" width="400" height="30" fill="var(--ink)" opacity="0.1" />
    </svg>);
}

const SCENE_CONFIG = {
  'hero-aerial': { sky: ['#fdf6e6', '#f3e2ba'], sun: [320, 60, 'var(--gold)'], seed: 1, water: false, trees: [{ x: 60, y: 232, scale: 1.3 }, { x: 340, y: 236, scale: 1.1 }, { x: 200, y: 240, scale: 0.9 }] },
  entrance: { sky: ['#fbeed2', '#e9c88a'], sun: [90, 50, 'var(--gold)'], seed: 2, water: false, trees: [{ x: 40, y: 228, scale: 1.2 }, { x: 360, y: 228, scale: 1.2 }], chapel: { x: 200, scale: 0.85 } },
  lake: { sky: ['#eef1e4', '#d8e0cc'], sun: [340, 45, '#fff6dc'], seed: 5, water: true, trees: [{ x: 40, y: 224, scale: 1.1 }, { x: 370, y: 226, scale: 0.9 }] },
  'chapel-facade': { sky: ['#faefd6', '#e7cf9c'], sun: [70, 55, 'var(--gold)'], seed: 3, water: false, trees: [{ x: 320, y: 230, scale: 1 }], chapel: { x: 190, scale: 1.25 } },
  'family-pathway': { sky: ['#f4efe0', '#dfd9b8'], sun: [330, 55, 'var(--terracotta)'], seed: 4, water: false, trees: [{ x: 50, y: 226, scale: 1.1 }, { x: 300, y: 230, scale: 1 }, { x: 210, y: 234, scale: 0.8 }], markers: 5 },
  'mausoleum-row': { sky: ['#f0e9d6', '#d9c79f'], sun: [60, 50, 'var(--gold)'], seed: 6, water: false, trees: [{ x: 30, y: 226, scale: 0.9 }], monuments: [90, 175, 260, 340] },
  'aerial-map': { sky: ['#fbf6ea', '#e4dcb9'], sun: [230, 40, 'var(--gold)'], seed: 7, water: true, trees: [{ x: 60, y: 220, scale: 1 }, { x: 130, y: 232, scale: 0.8 }, { x: 300, y: 224, scale: 1 }] },
  aborlan: { sky: ['#eef4ea', '#d5e2c9'], sun: [70, 42, '#fff2cf'], seed: 8, water: true, trees: [{ x: 40, y: 220, scale: 1.3 }, { x: 90, y: 232, scale: 1 }, { x: 330, y: 222, scale: 1.2 }, { x: 370, y: 234, scale: 0.9 }] },
  roxas: { sky: ['#f6f0dc', '#e3d3a2'], sun: [330, 48, 'var(--gold)'], seed: 9, water: true, trees: [{ x: 45, y: 224, scale: 1.4 }, { x: 360, y: 226, scale: 1.2 }] },
  lawn: { sky: ['#f6efdd', '#e3d6ac'], sun: [70, 46, 'var(--gold)'], seed: 10, water: false, trees: [{ x: 320, y: 230, scale: 1 }], markers: 3 },
  'premium-lawn': { sky: ['#eef3e6', '#d7e1c6'], sun: [330, 46, '#fff2cf'], seed: 11, water: true, trees: [{ x: 50, y: 224, scale: 1.2 }], markers: 2 },
  family: { sky: ['#f5efdc', '#e0d2a4'], sun: [60, 48, 'var(--gold)'], seed: 12, water: false, trees: [{ x: 340, y: 228, scale: 1 }], markers: 4 },
  niche: { sky: ['#f7f0dd', '#e5d6a8'], sun: [90, 42, 'var(--gold)'], seed: 13, water: false, trees: [], chapel: { x: 200, scale: 0.9 } },
  'premium-niche': { sky: ['#f7f0dd', '#ecd68f'], sun: [90, 46, 'var(--gold)'], seed: 14, water: false, trees: [], chapel: { x: 200, scale: 1.05 } },
  mausoleum: { sky: ['#f2ead2', '#dcc079'], sun: [70, 50, 'var(--gold)'], seed: 15, water: false, trees: [{ x: 60, y: 228, scale: 0.9 }], chapel: { x: 220, scale: 1.15 }, monuments: [90] },
  // Per-plot images for the price-list form's order summary + confirmation email —
  // one unique scene per real plot, richer as the price tier goes up.
  'regular':        { sky: ['#f6efdd', '#e3d6ac'], sun: [70, 46, 'var(--gold)'], seed: 10, water: false, trees: [{ x: 320, y: 230, scale: 1 }], markers: 3 },
  'premium':        { sky: ['#f2ecd8', '#ddd0a0'], sun: [100, 44, '#c9a355'], seed: 16, water: false, trees: [{ x: 60, y: 226, scale: 1.1 }, { x: 330, y: 228, scale: 0.9 }], markers: 3 },
  'corner-premium': { sky: ['#eef1de', '#d9d3a0'], sun: [300, 42, '#c9a355'], seed: 18, water: false, trees: [{ x: 50, y: 224, scale: 1 }, { x: 340, y: 230, scale: 1.1 }], monuments: [200], markers: 2 },
  'garden-regular': { sky: ['#eef4e5', '#d7e4c4'], sun: [80, 44, 'var(--gold)'], seed: 20, water: false, trees: [{ x: 70, y: 222, scale: 1.2 }, { x: 150, y: 230, scale: 0.9 }, { x: 330, y: 224, scale: 1.1 }], markers: 3 },
  'garden-premium': { sky: ['#eaf2e2', '#cfe0bd'], sun: [310, 44, '#fff2cf'], seed: 22, water: true, trees: [{ x: 50, y: 222, scale: 1.2 }, { x: 130, y: 232, scale: 0.9 }, { x: 320, y: 224, scale: 1.1 }], markers: 2 },
  'garden-corner':  { sky: ['#e7f1de', '#c9ddb5'], sun: [320, 42, '#fff2cf'], seed: 24, water: true, trees: [{ x: 40, y: 220, scale: 1.3 }, { x: 100, y: 230, scale: 1 }, { x: 300, y: 222, scale: 1.2 }, { x: 360, y: 232, scale: 0.9 }], monuments: [200], markers: 2 },
  'family-vault':   { sky: ['#f5efdc', '#e0d2a4'], sun: [60, 48, 'var(--gold)'], seed: 12, water: false, trees: [{ x: 340, y: 228, scale: 1 }], chapel: { x: 190, scale: 0.7 }, markers: 4 }
};

function Scene({ variant }) {
  if (variant === 'hero-voyage') return <HeroVoyageScene />;
  const cfg = SCENE_CONFIG[variant] || SCENE_CONFIG.lawn;
  const gid = `sky-${variant}`;
  return (
    <svg className="scene-svg" viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice" aria-hidden="true">
      <defs>
        <linearGradient id={gid} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={cfg.sky[0]} />
          <stop offset="100%" stopColor={cfg.sky[1]} />
        </linearGradient>
      </defs>
      <rect width="400" height="300" fill={`url(#${gid})`} />
      <circle cx={cfg.sun[0]} cy={cfg.sun[1]} r="44" fill={cfg.sun[2]} opacity="0.3" />
      <rect x="0" y="150" width="400" height="70" fill={cfg.sun[2]} opacity="0.1" />
      <path d={hillsPath(cfg.seed, 16, 188)} fill="var(--accent)" opacity="0.15" />
      <path d={hillsPath(cfg.seed + 3, 13, 208)} fill={FOLIAGE_DARK} opacity="0.55" />
      <path d={hillsPath(cfg.seed + 1, 9, 228)} fill={FOLIAGE} />
      {cfg.water && <WaterBand y={226} height={30} />}
      {(cfg.monuments || []).map((mx, i) => <Monument key={i} x={mx} y={230} scale={1} color="var(--ink)" />)}
      {cfg.chapel && <ChapelSilhouette x={cfg.chapel.x} y={230} scale={cfg.chapel.scale} color="var(--ink)" />}
      {(cfg.trees || []).map((t, i) => <TreeCluster key={i} {...t} color={i % 2 ? FOLIAGE_DARK : FOLIAGE} />)}
      {cfg.markers && <MarkerRow y={234} count={cfg.markers} color="var(--card)" />}
      <rect x="0" y="270" width="400" height="30" fill="var(--ink)" opacity="0.06" />
    </svg>);

}
function CountUp({ text }) {
  const ref = useRef(null);
  const [display, setDisplay] = useState(text);

  useEffect(() => {
    setDisplay(text);
    const match = text.match(/\d+/);
    if (!match || !ref.current) return;
    const target = parseInt(match[0], 10);
    const before = text.slice(0, match.index);
    const after = text.slice(match.index + match[0].length);
    let started = false;
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting && !started) {
          started = true;
          const start = performance.now();
          const dur = 900;
          const tick = (now) => {
            const p = Math.min(1, (now - start) / dur);
            const eased = 1 - Math.pow(1 - p, 3);
            setDisplay(`${before}${Math.round(target * eased)}${after}`);
            if (p < 1) requestAnimationFrame(tick);
          };
          requestAnimationFrame(tick);
        }
      });
    }, { threshold: 0.4 });
    io.observe(ref.current);
    return () => io.disconnect();
  }, [text]);

  return <span className="count-num" ref={ref}>{display}</span>;
}

// Local photos for plot cards — merged in by id regardless of whether tier
// data came from Firestore or the hardcoded fallback above, since the
// Firestore 'products' docs don't have an image field yet.
const LOCAL_PLOT_IMAGES = {
  'regular': '/regular_plot.jpg',
  'premium': '/premium_plot.jpg',
  'corner-premium': '/corner_premium_plot.jpg',
  'garden-regular': '/regular_garden_plot.jpg',
  'garden-premium': '/premium_garden_plot.jpg',
  'garden-corner': '/corner_prime_garden_plot.jpg',
  'family-vault': '/family_vault.png',
  'valor': '/valor_plot.png'
};

/* ---------- App ---------- */
function App() {
  const [t, setTweak] = useTweaks(window.TWEAK_DEFAULTS);
  const [baseTiers, setBaseTiers] = useState(BASE_TIERS);
  const [selectedTierId, setSelectedTierId] = useState(null);
  const [portalOpen, setPortalOpen] = useState(false);
  const [paymentNotice, setPaymentNotice] = useState(null);
  const [presale, setPresale] = useState(null); // null = closed, otherwise { type }

  // Detect PayMongo payment returns (?payment_status=success|cancelled)
  useEffect(() => {
    try {
      const params = new URLSearchParams(window.location.search);
      const status = params.get('payment_status');
      const type = params.get('type');
      const ref = params.get('ref');
      if (status === 'success') {
        setPaymentNotice({
          status: 'success',
          type,
          ref,
          message: type === 'presale_reservation'
            ? `Reservation deposit received! Your plot reservation (Ref: ${ref || 'Presale'}) is secured. Our team will contact you shortly.`
            : `Payment received! Your payment via PayMongo has been recorded and credited to your plan.`
        });
        if (type === 'portal_installment') setPortalOpen(true);
        window.history.replaceState(null, '', window.location.pathname + window.location.hash);
      } else if (status === 'cancelled') {
        setPaymentNotice({
          status: 'cancelled',
          message: 'PayMongo checkout was cancelled. No charges were made.'
        });
        window.history.replaceState(null, '', window.location.pathname + window.location.hash);
      }
    } catch (e) {}
  }, []);

  // The presale lives on its own page (/presale) and opens in its own browser tab.
  // Old links to /#presale are sent there.
  const goPresale = (type) => {
    const url = '/presale' + (type ? '?type=' + encodeURIComponent(type) : '');
    const w = window.open(url, '_blank');
    if (!w) window.location.assign(url); // pop-up blocked: open it in this tab instead
  };
  useEffect(() => {
    const check = () => { if (window.location.hash === '#presale') window.location.replace('/presale'); };
    check();
    window.addEventListener('hashchange', check);
    return () => window.removeEventListener('hashchange', check);
  }, []);
  const closePresale = () => {
    setPresale(null);
    if (window.location.hash === '#presale') {
      try { window.history.replaceState(null, '', window.location.pathname + window.location.search); } catch (err) { /* ignore */ }
    }
  };

  // Cart: persisted to localStorage so it survives a refresh, but never
  // touches Firestore until the customer actually submits an order.
  const [cart, setCart] = useState(() => {
    try {
      const raw = window.localStorage.getItem(CART_STORAGE_KEY);
      return raw ? JSON.parse(raw) : [];
    } catch (err) {
      return [];
    }
  });
  const [cartOpen, setCartOpen] = useState(false);

  useEffect(() => {
    try { window.localStorage.setItem(CART_STORAGE_KEY, JSON.stringify(cart)); } catch (err) { /* ignore */ }
  }, [cart]);

  const addToCart = (tier, qty = 1) => {
    setCart((prev) => {
      const existing = prev.find((i) => i.id === tier.id);
      if (existing) {
        return prev.map((i) => i.id === tier.id ? { ...i, qty: i.qty + qty } : i);
      }
      return [...prev, { id: tier.id, name: tier.name, price: tier.price, category: tier.category, image: tier.image, qty }];
    });
    setCartOpen(true);
  };
  const updateQty = (id, qty) => setCart((prev) => {
    if (qty <= 0) return prev.filter((i) => i.id !== id);
    return prev.map((i) => i.id === id ? { ...i, qty } : i);
  });
  const removeFromCart = (id) => setCart((prev) => prev.filter((i) => i.id !== id));
  const clearCart = () => setCart([]);
  const cartCount = cart.reduce((sum, i) => sum + i.qty, 0);

  // Load products from Firestore (falls back to hardcoded BASE_TIERS if DB is empty)
  useEffect(() => {
    if (!window.db) return;
    window.db.collection('products').orderBy('sortOrder').get()
      .then(snap => {
        if (!snap.empty) {
          const fromDb = snap.docs.map(d => ({ id: d.id, ...d.data() }));
          // Valor Plot always shows, even before it's added to Firestore.
          // Once a 'valor' product exists in Firestore, that one wins.
          if (!fromDb.some(t => t.id === 'valor')) {
            const valor = BASE_TIERS.find(t => t.id === 'valor');
            const at = fromDb.findIndex(t => t.id === 'family-vault');
            if (valor) fromDb.splice(at >= 0 ? at + 1 : fromDb.length, 0, valor);
          }
          setBaseTiers(fromDb);
        }
      })
      .catch(err => console.warn('[GH] Using default products (DB not set up yet):', err));
  }, []);

  // Apply palette CSS vars to :root
  useEffect(() => {
    const key = Array.isArray(t.palette) ? t.palette[0] : t.palette;
    const vars = PALETTES[key] || PALETTES['#2f5d4c'];
    Object.entries(vars).forEach(([k, v]) => document.documentElement.style.setProperty(k, v));
  }, [t.palette]);

  const priceMult = t.priceMultiplier / 100;

  // Apply tier prices
  const tiers = useMemo(() => baseTiers.map((tier) => ({
    ...tier,
    price: tier.price * priceMult,
    image: tier.image || LOCAL_PLOT_IMAGES[tier.id]
  })), [baseTiers, priceMult]);

  // Scroll fade-in
  useEffect(() => {
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {if (e.isIntersecting) e.target.classList.add('in');});
    }, { threshold: 0.1 });
    document.querySelectorAll('.fade-up').forEach((el) => io.observe(el));
    return () => io.disconnect();
  }, []);

  return (
    <PlanSelectionContext.Provider value={{ selectedTierId, selectTier: setSelectedTierId }}>
    <window.PresaleContext.Provider value={{ openPresale: goPresale }}>
    <CartContext.Provider value={{ cart, addToCart, removeFromCart, updateQty, clearCart, cartCount, cartOpen, setCartOpen }}>
      <Velaris bg="#f6f1e8" colors={['#e6d9b8', '#d9c48f', '#8fae9c', '#2f5d4c']} speed={0.5} grain={0.12}
        style={{ position: 'fixed', zIndex: 0 }} />
      <div style={{ position: 'relative', zIndex: 1 }}>
        <LightboxRoot>
          <Header />
          <main>
            <Hero onOpenPortal={() => setPortalOpen(true)} />
          <Tiers tiers={tiers} />
          <Mausoleum />
          <Gallery />
          <PaymentPlans tiers={tiers} />
          <PrePostNeed />
          <About />
          <Brochure tiers={tiers} />
          <FAQ />
        </main>
        <Footer />
        {paymentNotice && (
          <div style={{
            position: 'fixed', top: 20, left: '50%', transform: 'translateX(-50%)',
            zIndex: 9999, background: paymentNotice.status === 'success' ? '#2f5d4c' : '#7f2323',
            color: '#fff', padding: '14px 20px', borderRadius: 12, boxShadow: '0 12px 35px rgba(0,0,0,0.3)',
            display: 'flex', alignItems: 'center', gap: 12, maxWidth: 540, width: '92%', animation: 'fadeIn .3s ease'
          }}>
            <span style={{ fontSize: 24 }}>{paymentNotice.status === 'success' ? '✅' : 'ℹ️'}</span>
            <div style={{ flex: 1, fontSize: 14, lineHeight: 1.4 }}>{paymentNotice.message}</div>
            <button
              onClick={() => setPaymentNotice(null)}
              aria-label="Dismiss notice"
              style={{ background: 'transparent', border: 'none', color: '#fff', fontSize: 18, cursor: 'pointer', padding: '4px 8px' }}>
              ✕
            </button>
          </div>
        )}
        <CustomerPortal open={portalOpen} onClose={() => setPortalOpen(false)} />
        <CartDrawer open={cartOpen} onClose={() => setCartOpen(false)} />
        <Tweaks t={t} setTweak={setTweak} priceMult={priceMult} />
        {presale && <window.PresaleFlow tiers={tiers} startType={presale.type} onClose={closePresale} />}
      </LightboxRoot>
      </div>
    </CartContext.Provider>
    </window.PresaleContext.Provider>
    </PlanSelectionContext.Provider>);
}

/* ---------- Header ---------- */
function Header() {
  const [scrolled, setScrolled] = useState(0);
  const [mobileOpen, setMobileOpen] = useState(false);
  const { cart, cartCount, setCartOpen } = useCart();
  useBodyScrollLock(mobileOpen);
  useEffect(() => {
    if (!mobileOpen) return;
    const onKey = (e) => { if (e.key === 'Escape') setMobileOpen(false); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [mobileOpen]);
  useEffect(() => {
    const onScroll = () => {
      // fades from fully opaque at the top down to ~80% opaque over the first 160px of scroll
      const p = Math.min(1, window.scrollY / 160);
      setScrolled(p);
    };
    window.addEventListener('scroll', onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  const headerBgOpacity = 1 - scrolled * 0.2; // never drops below 80% opaque, so text stays readable
  const compact = scrolled > 0.2; // once you're ~30px into scrolling, header collapses to one row

  // Continuous values (not just on/off) so sizes ease smoothly with scroll instead of snapping
  const navPadY = 7 - scrolled * 1;
  const navPadX = 14 - scrolled * 4;
  const navFontSize = 14 - scrolled * 1.5;
  const navGap = 10 - scrolled * 4;
  const rowGap = 20 - scrolled * 8;
  const btnPadY = 10 - scrolled * 1;
  const btnPadX = 28 - scrolled * 8;

  const navItems = [
    { href: '/presale', newTab: true, label: 'Presale', icon: (
      <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><path d="M20.6 13.4 13.4 20.6a2 2 0 0 1-2.8 0L3 13V3h10l7.6 7.6a2 2 0 0 1 0 2.8z"/><circle cx="7.5" cy="7.5" r="1.2"/></svg>
    ) },
    { href: '/my-payments', newTab: true, label: 'My payments', icon: (
      <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M3 10h18"/><path d="M7 15h4"/></svg>
    ) },
    { href: '#tiers', label: 'Plots & Pricing', icon: (
      <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
    ) },
    { href: '#mausoleum', label: 'Mausoleum', icon: (
      <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><path d="M4 21V9l8-5 8 5v12"/><line x1="9" y1="21" x2="9" y2="13"/><line x1="15" y1="21" x2="15" y2="13"/><line x1="2" y1="21" x2="22" y2="21"/></svg>
    ) },
    { href: '#plans', label: 'Payment Plans', icon: (
      <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><rect x="2" y="5" width="20" height="14" rx="2"/><line x1="2" y1="10" x2="22" y2="10"/><line x1="6" y1="15" x2="10" y2="15"/></svg>
    ) },
    { href: '#faq', label: 'FAQ', icon: (
      <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="9"/><path d="M9.5 9a2.5 2.5 0 0 1 4.9.7c0 1.8-2.4 2-2.4 3.6"/><line x1="12" y1="17" x2="12" y2="17"/></svg>
    ) }
  ];

  return (
    <header className="site" style={{
      padding: '10px 0',
      position: 'sticky', top: 0, zIndex: 50,
      backgroundColor: `rgba(var(--bg-rgb, 246,241,232), ${headerBgOpacity})`,
      backdropFilter: scrolled > 0.05 ? 'blur(8px)' : 'none',
      transition: 'background-color .2s ease, backdrop-filter .2s ease',
      boxShadow: scrolled > 0.1 ? '0 2px 12px rgba(0,0,0,.06)' : 'none'
    }}>
      <style>{`
        .nav-link-icon { transition: background .15s ease, border-color .15s ease; }
        .nav-link-icon:hover { background: var(--card); border-color: var(--accent) !important; }
        @keyframes metalShimmer {
          0%   { background-position: 0% 50%; }
          50%  { background-position: 100% 50%; }
          100% { background-position: 0% 50%; }
        }
        @keyframes metalSweep {
          0%   { transform: translateX(-130%) skewX(-20deg); }
          100% { transform: translateX(230%) skewX(-20deg); }
        }
        .liquid-metal-btn {
          background: linear-gradient(100deg,
            #8a6a2f 0%, #d9b563 18%, #f4e2a8 32%, #b08544 48%,
            #3f6e58 62%, #4f8a6e 74%, #2f5d4c 88%, #8a6a2f 100%);
          background-size: 260% 100%;
          animation: metalShimmer 6s ease-in-out infinite;
        }
        .liquid-metal-btn:hover { animation-duration: 2.2s; }
        .lm-sweep {
          position: absolute; top: -40%; left: 0; width: 26%; height: 180%;
          background: linear-gradient(90deg, transparent, rgba(255,255,255,.55), transparent);
          opacity: 0; pointer-events: none;
        }
        .liquid-metal-btn:hover .lm-sweep { opacity: 1; animation: metalSweep 1.1s ease-in-out; }
        .header-row { transition: gap .3s ease; }
        .nav-wrap { transition: opacity .25s ease, transform .25s ease, flex-basis .3s ease, margin-top .3s ease; }
      `}</style>
      <div className="wrap">
        <div className="header-row" style={{
            display: 'flex', alignItems: 'center', flexWrap: 'nowrap',
            justifyContent: 'space-between', gap: rowGap,
            transition: 'gap .35s cubic-bezier(.25,.1,.25,1)'
          }}>
          <a href="#" className="brand" style={{ textDecoration: 'none', order: 0, flexShrink: 1, minWidth: 0 }}>
            <img src="/logo-header.png" alt="Golden Harmonic Memorial Park logo"
              style={{ width: 44, height: 44, flexShrink: 0, objectFit: 'contain' }} />
            <div className="brand-text">
              <div className="name">Golden Harmonic</div>
              <div className="sub">Memorial Park · Palawan · 📍 Palawan, Philippines</div>
            </div>
          </a>

          {/* Everything right of the logo lives in one flex group so justify-content:
              space-between on header-row only opens a single gap (logo <-> group),
              instead of spreading nav / CTA / cart apart from each other. */}
          <div className="nav-cta-group" style={{
              display: 'flex', alignItems: 'center', flexWrap: compact ? 'nowrap' : 'wrap',
              justifyContent: 'flex-end', gap: 16
            }}>
            <nav className="primary nav-wrap" style={{
                display: 'flex', alignItems: 'center', gap: navGap, flexWrap: compact ? 'nowrap' : 'wrap',
                order: compact ? 1 : 3,
                flexBasis: compact ? 'auto' : '100%',
                marginTop: compact ? 0 : 10,
                overflow: compact ? 'hidden' : 'visible',
                minWidth: 0,
                opacity: 1,
                transition: 'gap .35s cubic-bezier(.25,.1,.25,1), margin-top .35s cubic-bezier(.25,.1,.25,1)'
              }}>
              {navItems.map(item => (
                <a key={item.href} href={item.href} className="nav-link-icon" {...(item.newTab ? { target: '_blank', rel: 'noopener' } : {})}
                  style={{ display: 'inline-flex', alignItems: 'center', gap: 7, whiteSpace: 'nowrap',
                    padding: `${navPadY}px ${navPadX}px`, fontSize: navFontSize,
                    borderRadius: 9, textDecoration: 'none', color: 'var(--ink-2)',
                    background: 'rgba(0,0,0,.05)', border: '1px solid var(--line)',
                    transition: 'padding .35s cubic-bezier(.25,.1,.25,1), font-size .35s cubic-bezier(.25,.1,.25,1), background .15s ease, border-color .15s ease' }}>
                  <span style={{ display: 'inline-flex' }}>{item.icon}</span>
                  {item.label}
                </a>
              ))}
            </nav>

            <button
              type="button"
              className="nav-link-icon gh-cart-toggle"
              onClick={() => setCartOpen(true)}
              aria-label={`Open cart, ${cartCount} item${cartCount === 1 ? '' : 's'}`}
              style={{
                order: compact ? 2 : 1, flexShrink: 0, position: 'relative',
                display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                width: 38, height: 38, borderRadius: 9, cursor: 'pointer',
                background: 'rgba(0,0,0,.05)', border: '1px solid var(--line)', color: 'var(--ink-2)'
              }}>
              <svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
                <circle cx="9" cy="21" r="1" /><circle cx="20" cy="21" r="1" />
                <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6" />
              </svg>
              {cartCount > 0 && (
                <span style={{
                  position: 'absolute', top: -6, right: -6, minWidth: 18, height: 18, padding: '0 4px',
                  borderRadius: 9, background: 'var(--gold)', color: '#fffaf0',
                  fontSize: 10.5, fontWeight: 700, display: 'flex', alignItems: 'center', justifyContent: 'center',
                  fontFamily: 'Inter, sans-serif', boxShadow: '0 1px 3px rgba(0,0,0,.3)'
                }}>{cartCount}</span>
              )}
            </button>

            <button
              className={`mobile-menu-btn${mobileOpen ? ' open' : ''}`}
              onClick={() => setMobileOpen(o => !o)}
              aria-label={mobileOpen ? 'Close menu' : 'Open menu'}
              aria-expanded={mobileOpen}
              style={{ order: 4 }}>
              <span></span>
            </button>
          </div>
        </div>

        <div className={`mobile-nav-panel${mobileOpen ? ' open' : ''}`} onClick={() => setMobileOpen(false)}>
          <div className="inner" onClick={(e) => e.stopPropagation()}>
            {navItems.map(item => (
              <a key={item.href} href={item.href} {...(item.newTab ? { target: '_blank', rel: 'noopener' } : {})} onClick={() => setMobileOpen(false)}>
                <span style={{ display: 'inline-flex', marginRight: 10 }}>{item.icon}</span>
                {item.label}
              </a>
            ))}
            <a href="#brochure" className="btn btn-primary" onClick={() => setMobileOpen(false)}>
              Free price list
            </a>
          </div>
        </div>
      </div>
    </header>);
}  

/* ---------- Velaris: animated WebGL gradient background (adapted, no build step needed) ---------- */
function Velaris({ bg = '#f6f1e8', colors = ['#e6d9b8', '#d9c48f', '#8fae9c', '#2f5d4c'], speed = 0.5, grain = 0.12, height = '100%', style }) {
  const canvasRef = useRef(null);
  const containerRef = useRef(null);

  useEffect(() => {
    const canvas = canvasRef.current;
    const container = containerRef.current;
    if (!canvas || !container) return;
    const gl = canvas.getContext('webgl');
    if (!gl) return; // silently no-op on very old browsers, page still works fine

    const hexToRgb = (hex) => {
      const h = hex.replace('#', '');
      return [parseInt(h.slice(0,2),16)/255, parseInt(h.slice(2,4),16)/255, parseInt(h.slice(4,6),16)/255];
    };

    const vertexShaderGLSL = `
      attribute vec2 position;
      varying vec2 vUv;
      void main() {
        vUv = position * 0.5 + 0.5;
        gl_Position = vec4(position, 0.0, 1.0);
      }
    `;

    const fragmentShaderGLSL = `
      precision highp float;
      varying vec2 vUv;
      uniform vec2  u_resolution;
      uniform float u_time;
      uniform float u_grain;
      uniform vec3  u_colors[4];
      uniform vec3  u_bg;
      vec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); }
      float snoise(vec2 v){
        const vec4 C = vec4(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439);
        vec2 i  = floor(v + dot(v, C.yy) );
        vec2 x0 = v -   i + dot(i, C.xx);
        vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
        vec4 x12 = x0.xyxy + C.xxzz;
        x12.xy -= i1;
        i = mod(i, 289.0);
        vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 )) + i.x + vec3(0.0, i1.x, 1.0 ));
        vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
        m = m*m; m = m*m;
        vec3 x = 2.0 * fract(p * C.www) - 1.0;
        vec3 h = abs(x) - 0.5;
        vec3 ox = floor(x + 0.5);
        vec3 a0 = x - ox;
        m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
        vec3 g;
        g.x  = a0.x  * x0.x  + h.x  * x0.y;
        g.yz = a0.yz * x12.xz + h.yz * x12.yw;
        return 130.0 * dot(m, g);
      }
      void main() {
        vec2 uv = vUv;
        float ratio = u_resolution.x / u_resolution.y;
        vec2 p = uv - 0.5;
        p.x *= ratio;
        float t = u_time * 0.1;
        float n1 = snoise(p * 0.4 + vec2(t * 0.2, -t * 0.3));
        float n2 = snoise(p * 0.55 + vec2(-t * 0.15, t * 0.25) + n1 * 0.25);
        float n3 = snoise(p * 0.75 + vec2(t * 0.1, -t * 0.2) + n2 * 0.2);
        vec3 col = u_bg;
        float dist = length(p) * 1.5;
        float vignette = 1.0 - smoothstep(0.3, 1.2, dist);
        col = mix(col, u_colors[0], smoothstep(-0.2, 0.5, n1) * 0.6);
        col = mix(col, u_colors[1], smoothstep(-0.1, 0.6, n2) * 0.5);
        col = mix(col, u_colors[2], smoothstep(-0.3, 0.4, n3) * 0.4);
        col = mix(col, u_colors[3], smoothstep(0.0, 0.7, n1 * n2) * 0.15);
        float glow = smoothstep(0.8, 0.0, dist) * 0.15;
        col += u_colors[1] * glow;
        col = mix(col * 0.9, col, vignette);
        float grainV = fract(sin(dot(uv, vec2(12.9898, 78.233))) * 43758.5453 + u_time);
        col += (grainV - 0.5) * u_grain * 0.06;
        gl_FragColor = vec4(col, 1.0);
      }
    `;

    const createShader = (type, src) => {
      const s = gl.createShader(type);
      gl.shaderSource(s, src);
      gl.compileShader(s);
      return s;
    };

    const program = gl.createProgram();
    gl.attachShader(program, createShader(gl.VERTEX_SHADER, vertexShaderGLSL));
    gl.attachShader(program, createShader(gl.FRAGMENT_SHADER, fragmentShaderGLSL));
    gl.linkProgram(program);
    gl.useProgram(program);

    const buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1, 1,-1, -1,1, 1,1]), gl.STATIC_DRAW);

    const posLoc = gl.getAttribLocation(program, 'position');
    gl.enableVertexAttribArray(posLoc);
    gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);

    const locs = {
      res: gl.getUniformLocation(program, 'u_resolution'),
      time: gl.getUniformLocation(program, 'u_time'),
      grain: gl.getUniformLocation(program, 'u_grain'),
      colors: gl.getUniformLocation(program, 'u_colors'),
      bg: gl.getUniformLocation(program, 'u_bg'),
    };

    const resize = () => {
      const dpr = Math.min(window.devicePixelRatio || 1, 2);
      canvas.width = Math.max(1, container.clientWidth * dpr);
      canvas.height = Math.max(1, container.clientHeight * dpr);
      gl.viewport(0, 0, canvas.width, canvas.height);
    };
    const ro = new ResizeObserver(resize);
    ro.observe(container);
    resize();

    let raf;
    const render = (t) => {
      gl.uniform2f(locs.res, canvas.width, canvas.height);
      gl.uniform1f(locs.time, t * 0.001 * speed);
      gl.uniform1f(locs.grain, grain);
      gl.uniform3f(locs.bg, ...hexToRgb(bg));
      const flat = new Float32Array(colors.slice(0, 4).flatMap(hexToRgb));
      gl.uniform3fv(locs.colors, flat);
      gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
      raf = requestAnimationFrame(render);
    };
    raf = requestAnimationFrame(render);
    return () => { ro.disconnect(); cancelAnimationFrame(raf); };
  }, [bg, colors, speed, grain]);

  return (
    <div ref={containerRef} style={{ position: 'absolute', inset: 0, height, width: '100%', overflow: 'hidden', ...style }}>
      <canvas ref={canvasRef} style={{ pointerEvents: 'none', position: 'absolute', inset: 0, width: '100%', height: '100%' }} />
    </div>
  );
}

function Hero({ onOpenPortal }) {
  return (
    <section className="hero" style={{ position: 'relative', overflow: 'hidden' }}>
      <style>{`
        .hero-account-badge:hover{ background: #9c7443; }
        .hero-btn-ghost{
          border: 2px solid var(--accent);
          background: rgba(255,255,255,0.55);
          backdrop-filter: blur(4px);
          -webkit-backdrop-filter: blur(4px);
          color: var(--accent);
        }
        .hero-btn-ghost:hover{
          background: var(--accent);
          color: var(--accent-ink);
        }
      `}</style>
      <div className="wrap hero-grid" style={{ position: 'relative', zIndex: 1 }}>
        <div className="fade-up in">
          <span className="eyebrow mono">9°29′N 118°30′E · Palawan, Philippines</span>
          <h1>A sanctuary of peace,<br />heritage, and <em>remembrance</em>.</h1>
          <p className="lede">
            Your legacy, our sacred care. Twenty-five hectares of garden, riverside groves, and family estates cut into Palawan's own limestone coastline — designed for its families to remember well, for generations.
          </p>

          <div className="cta-row" style={{ marginBottom: 14 }}>
            <a href="#brochure" className="btn liquid-metal-btn" style={{
                position: 'relative', display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                overflow: 'hidden', borderRadius: 10, padding: '14px 28px',
                color: '#fffaf0', fontWeight: 700, fontSize: 15, textDecoration: 'none',
                whiteSpace: 'nowrap', fontFamily: 'Inter, sans-serif',
                boxShadow: '0 4px 14px rgba(0,0,0,.2), inset 0 1px 0 rgba(255,255,255,.3)',
                textShadow: '0 1px 2px rgba(0,0,0,.35)'
              }}>
              <span className="lm-sweep"></span>
              <span style={{ position: 'relative', zIndex: 1 }}>Get the full price list</span>
            </a>
            <a href="#tiers" className="btn btn-ghost hero-btn-ghost">View plots &amp; pricing →</a>
          </div>

          <div style={{ marginTop: 22, paddingTop: 18, borderTop: '1px solid var(--line)', fontSize: 13, color: 'var(--ink-2)', maxWidth: 460, display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
            <span>Already reserved a plot with us?</span>
            <button onClick={onOpenPortal} className="hero-account-badge"
              style={{ display: 'inline-flex', alignItems: 'center', gap: 6, background: 'var(--gold)', border: 'none',
                fontFamily: 'inherit', fontSize: 13, fontWeight: 700, color: '#fff', cursor: 'pointer',
                padding: '7px 14px', borderRadius: 999, boxShadow: '0 4px 12px rgba(176,133,68,.35)' }}>
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="8" r="4"/><path d="M4 21c0-4 3.6-7 8-7s8 3 8 7"/></svg>
              My Account
            </button>
          </div>

          <div className="meta">
            <div>
              <div className="label">Total hectares</div>
              <div className="value">4.9 ha</div>
            </div>
            <div>
              <div className="label">Plots under perpetual care</div>
              <div className="value">14,978</div>
            </div>
          </div>
        </div>
        <div className="fade-up in">
          <Photo aspect="aspect-hero" image="/hero-tabon-cave.jpg" imagePosition="60% 55%" label="the soul's passage — after Palawan's Tabon Cave funerary boats"
            items={[{ label: 'the soul\'s passage', caption: 'A motif carried from the Tabon Caves — Palawan\'s earliest funerary art, and the same lineage later carved into the Manunggul jar.' }]} />
        </div>
      </div>
    </section>);

}

/* ---------- Trust strip ---------- */
function Trust() {
  const items = [
  { big: 'DHSUD', lbl: 'Licensed & permit-cleared' },
  { big: '100%', lbl: 'Perpetual care fund' },
  { big: '8–12%', lbl: 'Annual lot appreciation' }];

  return (
    <div className="trust">
      <div className="wrap row">
        {items.map((i, idx) =>
        <div className="item fade-up in" key={i.lbl} style={{ '--stagger': idx }}>
            <div className="big"><CountUp text={i.big} /></div>
            <div className="lbl">{i.lbl}</div>
          </div>
        )}
      </div>
    </div>);

}

/* ---------- Pricing tiers ---------- */
/* ---------- Detect if running as an installed PWA (not a regular browser tab) ---------- */
function useIsPWA() {
  const [isPWA, setIsPWA] = useState(false);
  useEffect(() => {
    const standalone =
      window.matchMedia('(display-mode: standalone)').matches ||
      window.navigator.standalone === true; // iOS Safari
    setIsPWA(standalone);
  }, []);
  return isPWA;
}

/* ---------- Featured carousel (drag/swipe card stack) ---------- */
function CarouselSlider({ slides = [] }) {
  const [index, setIndex] = useState(0);
  const [direction, setDirection] = useState(1);
  const [dragX, setDragX] = useState(0);
  const dragging = React.useRef(false);
  const startX = React.useRef(0);

  const paginate = (dir) => {
    setDirection(dir);
    setIndex((p) => (p + dir + slides.length) % slides.length);
  };

  const onPointerDown = (e) => {
    dragging.current = true;
    startX.current = (e.touches ? e.touches[0].clientX : e.clientX);
  };
  const onPointerMove = (e) => {
    if (!dragging.current) return;
    const x = (e.touches ? e.touches[0].clientX : e.clientX);
    setDragX(x - startX.current);
  };
  const endDrag = () => {
    if (!dragging.current) return;
    dragging.current = false;
    if (dragX < -80) paginate(1);
    else if (dragX > 80) paginate(-1);
    setDragX(0);
  };

  if (!slides.length) return null;
  const slide = slides[index];

  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
      <style>{`
        .cs-stack { position: relative; width: 260px; aspect-ratio: 1; transform: rotate(-6deg); }
        .cs-bg { position: absolute; inset: 4%; background: var(--card); border-radius: 40px; border: 6px solid var(--card); transform: scale(.95); opacity: .5; }
        .cs-card {
          position: absolute; inset: 0; background: var(--card); border-radius: 40px; padding: 8px;
          box-shadow: 0 8px 24px rgba(20,18,12,.14); border: 1.2px solid var(--line);
          overflow: hidden; cursor: grab; touch-action: pan-y;
          transition: transform .45s cubic-bezier(.16,1,.3,1), opacity .35s ease, filter .35s ease;
        }
        .cs-card:active { cursor: grabbing; }
        .cs-inner { width: 100%; height: 100%; border-radius: 32px; overflow: hidden; position: relative; background: var(--bg-2); }
        .cs-heart {
          position: absolute; top: 14px; right: 14px; width: 38px; height: 38px; border-radius: 999px;
          background: rgba(255,255,255,.85); backdrop-filter: blur(6px); border: 1px solid rgba(255,255,255,.4);
          display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: 0 2px 6px rgba(0,0,0,.12);
        }
        .cs-label {
          position: absolute; left: 0; right: 0; bottom: 0; padding: 14px 16px;
          background: linear-gradient(180deg, transparent, rgba(20,18,12,.55));
          color: #fff; font-family: 'Newsreader', serif;
        }
        .cs-dots { display: flex; gap: 10px; margin-top: 28px; transform: rotate(-6deg); }
        .cs-dot { width: 10px; height: 10px; border-radius: 999px; background: var(--gold); cursor: pointer; transition: all .25s ease; }
      `}</style>

      <div className="cs-stack">
        <div className="cs-bg"></div>
        <div
          className="cs-card"
          onMouseDown={onPointerDown} onMouseMove={onPointerMove} onMouseUp={endDrag} onMouseLeave={endDrag}
          onTouchStart={onPointerDown} onTouchMove={onPointerMove} onTouchEnd={endDrag}
          style={{ transform: `translateX(${dragX}px) rotate(${dragX / 15}deg)` }}>
          <div className="cs-inner">
            <Scene variant={slide.scene} />
            <div className="cs-label">
              <div style={{ fontSize: 18 }}>{slide.name}</div>
              <div style={{ fontSize: 12, opacity: .85 }}>{fmt(slide.price)}</div>
            </div>
            <div className="cs-heart" title="Shortlist this plot">
              <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#2f5d4c" strokeWidth="1.8"><path d="M20.8 4.6a5.5 5.5 0 0 0-7.8 0L12 5.6l-1-1a5.5 5.5 0 0 0-7.8 7.8l1 1L12 21l7.8-7.6 1-1a5.5 5.5 0 0 0 0-7.8z"/></svg>
            </div>
          </div>
        </div>
      </div>

      <div className="cs-dots">
        {slides.map((_, i) => (
          <div key={i} className="cs-dot"
            style={{ opacity: i === index ? 1 : .35, transform: i === index ? 'scale(1.2)' : 'scale(1)' }}
            onClick={() => { setDirection(i > index ? 1 : -1); setIndex(i); }}>
          </div>
        ))}
      </div>
    </div>);
}

function Tiers({ tiers }) {
  const isPWA = useIsPWA();
  const categories = ['Regular Plots', 'Garden Plots', 'Family Vault'];
  return (
    <section className="block" id="tiers">
      <style>{`
        /* ── Cinematic plot card animations ──────────────────────────────
           Entrance: fade + rise + subtle scale, staggered left-to-right.
           Hover: image Ken Burns zoom, gradient/glow, whole-card lift.
           Selected: soft scale + glow highlight.
           All additive — none of this touches existing layout, colors,
           typography, pricing, or card structure. */
        .tier-cine-card {
          opacity: 0;
          transform: translateY(22px) scale(.965);
          transition: opacity .9s cubic-bezier(.16,1,.3,1), transform .9s cubic-bezier(.16,1,.3,1);
          transition-delay: calc(var(--stagger, 0) * 90ms);
        }
        .tier.in .tier-cine-card { opacity: 1; transform: translateY(0) scale(1); }

        .tier:hover .tier-cine-card {
          transform: translateY(-5px) scale(1);
          box-shadow: 0 18px 34px rgba(20,18,12,.14);
        }
        .tier.in:hover .tier-cine-card { transition-delay: 0s; }

        .tier.selected .tier-cine-card {
          transform: translateY(-2px) scale(1.015);
          box-shadow: 0 0 0 2px var(--accent), 0 20px 40px rgba(47,93,76,.2);
        }

        .cine-frame { position: relative; overflow: hidden; border-radius: inherit; }
        .cine-zoom { transition: transform .7s cubic-bezier(.16,1,.3,1); transform-origin: center; will-change: transform; }
        .tier:hover .cine-zoom { transform: scale(1.07); }

        .cine-vignette {
          position: absolute; inset: 0; pointer-events: none;
          background: linear-gradient(180deg, rgba(0,0,0,0) 55%, rgba(20,18,12,.24) 100%);
          opacity: .75; transition: opacity .6s ease;
        }
        .tier:hover .cine-vignette { opacity: 1; }

        .cine-glow {
          position: absolute; inset: 0; pointer-events: none; opacity: 0;
          background: radial-gradient(circle at 50% 30%, rgba(255,244,214,.22), transparent 62%);
          transition: opacity .6s ease;
        }
        .tier:hover .cine-glow { opacity: 1; }

        @media (prefers-reduced-motion: reduce) {
          .tier-cine-card, .cine-zoom, .cine-vignette, .cine-glow {
            transition: none !important; animation: none !important;
          }
        }
      `}</style>
      <div className="wrap">
        <div className="section-head fade-up">
          <div>
            <span className="eyebrow">Plots & Pricing</span>
            <h2 style={{ marginTop: 18 }}>Eight ways to plan ahead — from ₱30,000.</h2>
          </div>
          <div className="side">
            Every plot includes perpetual care and all-faiths welcome. No down payment on any installment plan — pay spot cash, or spread the (surcharged) total over 1, 2, 3, or 5 years.
          </div>
        </div>
        {categories.map((cat) => {
          const group = tiers.filter((t) => t.category === cat);
          if (!group.length) return null;
          return (
            <div key={cat} style={{ marginBottom: 12 }}>
              <h3 className="cat-head fade-up">{cat}</h3>
              {cat === 'Regular Plots' && isPWA && (
                <div className="fade-up" style={{ marginBottom: 32 }}>
                  <CarouselSlider slides={group.map((t) => ({
                    name: t.name, price: t.price,
                    scene: t.id === 'regular' ? 'lawn' : 'premium-lawn'
                  }))} />
                </div>
              )}
              <div className={`tiers ${group.length === 1 ? 'single' : ''}`}>
                {group.map((tier, i) => <Tier key={tier.id} tier={tier} i={i} allTiers={tiers} />)}
              </div>
            </div>);

        })}
        <p className="small" style={{ marginTop: 22, textAlign: 'center' }}>
          All prices in Philippine Pesos · Includes lot, perpetual care, and standard documentation
        </p>
      </div>
    </section>);

}

function tierSceneKey(t) {
  return t.id === 'family-vault' ? 'family' :
  t.id === 'regular' || t.id === 'garden-regular' ? 'lawn' :
  'premium-lawn';
}

function Tier({ tier, i = 0, allTiers = [] }) {
  const { selectedTierId, selectTier } = usePlanSelection();
  const { cart, addToCart } = useCart();
  const { openPresale } = React.useContext(window.PresaleContext);
  const isPresale = (window.PRESALE_TYPE_IDS || []).includes(tier.id);
  const inCartItem = cart.find((c) => c.id === tier.id);
  const [qty, setQty] = useState(1);
  const group = allTiers.length ?
  allTiers.map((t) => ({ label: t.label, caption: t.name, image: t.image, scene: t.image ? undefined : tierSceneKey(t) })) :
  [{ label: tier.label, caption: tier.name, image: tier.image, scene: tier.image ? undefined : tierSceneKey(tier) }];
  const idx = allTiers.findIndex((t) => t.id === tier.id);
  const isSelected = selectedTierId === tier.id;
  const fiveYear = PLAN_TERMS[4]; // lowest monthly = longest term
  const monthly = tier.price * (1 + fiveYear.surcharge) / fiveYear.months;
  const sceneKey = tierSceneKey(tier);
  return (
    <div
      className={`tier fade-up ${isSelected ? 'selected' : ''}`}
      style={{ '--stagger': i % 6 }}>

      <div className="tier-cine-card">
        <div className="cine-frame">
          <div className="cine-zoom">
            <Photo label={tier.label} image={tier.image} scene={tier.image ? undefined : sceneKey} items={group} index={idx < 0 ? 0 : idx} />
          </div>
          <div className="cine-vignette"></div>
          <div className="cine-glow"></div>
        </div>
        <div>
          <h3>{tier.name}</h3>
          <div className="desc">{tier.desc}</div>
        </div>
        <div className="price-row">
          <div>
            <div className="from">Spot cash</div>
            <div className="price"><span className="currency">₱</span>{Math.round(tier.price).toLocaleString('en-PH')}</div>
          </div>
        </div>
        <ul className="feature-list">
          {tier.features.map((f) => <li key={f}>{f}</li>)}
        </ul>
        {
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 10 }}>
          <div style={{
            display: 'flex', alignItems: 'center', border: '1px solid var(--line)',
            borderRadius: 8, overflow: 'hidden', flexShrink: 0
          }}>
            <button
              type="button"
              aria-label="Decrease quantity"
              onClick={() => setQty((q) => Math.max(1, q - 1))}
              style={{
                width: 32, height: 32, border: 'none', background: 'var(--card)',
                color: 'var(--ink)', fontSize: 16, cursor: 'pointer', lineHeight: 1
              }}>−</button>
            <span style={{
              width: 34, textAlign: 'center', fontSize: 13.5, fontWeight: 600, color: 'var(--ink)'
            }}>{qty}</span>
            <button
              type="button"
              aria-label="Increase quantity"
              onClick={() => setQty((q) => q + 1)}
              style={{
                width: 32, height: 32, border: 'none', background: 'var(--card)',
                color: 'var(--ink)', fontSize: 16, cursor: 'pointer', lineHeight: 1
              }}>+</button>
          </div>
          <button
            type="button"
            className="btn"
            style={{
              flex: 1, justifyContent: 'center', display: 'inline-flex', alignItems: 'center', gap: 6,
              background: inCartItem ? 'var(--accent)' : 'var(--gold)', color: '#fffaf0', border: 'none',
              fontWeight: 700, fontSize: 12.5, padding: '7px 12px', height: 32,
              boxShadow: '0 2px 8px rgba(0,0,0,.15)',
              lineHeight: 1.15, textAlign: 'center', whiteSpace: 'normal'
            }}
            onClick={() => { addToCart(tier, qty); setQty(1); }}>
            {inCartItem ?
            <span style={{ display: 'flex', flexDirection: 'column', gap: 0 }}>
                <span style={{ fontSize: 12 }}>✓ In cart ({inCartItem.qty})</span>
                <span style={{ fontSize: 10, fontWeight: 500, opacity: 0.85 }}>Add {qty} more</span>
              </span> :

            <>
                <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
                  <circle cx="9" cy="21" r="1" /><circle cx="20" cy="21" r="1" />
                  <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6" />
                </svg>
                {qty > 1 ? `Add ${qty} to Cart` : 'Add to Cart'}
              </>}

          </button>
        </div>}
        <a
          href="#plans"
          onClick={() => selectTier(tier.id)}
          style={{
            display: 'block', textAlign: 'center', marginTop: 10, fontSize: 12.5,
            color: 'var(--ink-2)', textDecoration: 'underline', textUnderlineOffset: 2
          }}>
          {isSelected ? '✓ Selected — see monthly terms below' : 'Not ready yet? See monthly payment options →'}
        </a>
      </div>
    </div>);

}

/* ---------- Payment calculator ---------- */
function PaymentCalculator({ tiers }) {
  const { selectedTierId, selectTier } = usePlanSelection();
  const [tierId, setTierId] = useState(tiers[0]?.id);
  const [termKey, setTermKey] = useState('1yr');

  // Reflect a plan chosen from the Plots & Pricing cards above
  useEffect(() => {
    if (selectedTierId) setTierId(selectedTierId);
  }, [selectedTierId]);

  const tier = tiers.find((t) => t.id === tierId) || tiers[0];
  const price = tier ? tier.price : 0;
  const term = PLAN_TERMS.find((t) => t.key === termKey) || PLAN_TERMS[0];
  const isCash = term.key === 'cash';
  const total = price * (1 + term.surcharge);
  const monthly = isCash ? 0 : total / term.months;
  const surchargeAmt = total - price;

  return (
    <div className="calc-card fade-up">
      <h3>{selectedTierId ? `Your plan: ${tier?.name || ''}` : 'Build your own plan'}</h3>
      <div className="calc-sub">
        {selectedTierId ?
        'Pick a payment term below — no down payment on any plan.' :
        'Pick a plot and a term — no down payment on any plan.'}
      </div>

      <div className="calc-row">
        <div className="calc-row-head"><label>Plot type</label></div>
        <select className="calc-select" value={tierId} onChange={(e) => { setTierId(e.target.value); selectTier(e.target.value); }}>
          {tiers.map((t) =>
          <option key={t.id} value={t.id}>{t.name} — {fmt(t.price)}</option>
          )}
        </select>
      </div>

      <div className="calc-row">
        <div className="calc-row-head"><label>Payment term</label></div>
        <div className="calc-toggle-row" style={{ flexWrap: 'wrap' }}>
          {PLAN_TERMS.map((tm) =>
          <div key={tm.key} className={`calc-chip ${termKey === tm.key ? 'active' : ''}`} onClick={() => setTermKey(tm.key)}>
              {tm.label}
            </div>
          )}
        </div>
      </div>

      <div className="calc-output">
        {isCash ?
        <>
            <div className="box"><div className="lbl">Spot cash price</div><div className="val">{fmt(price)}</div></div>
            <div className="box"><div className="lbl">Down payment</div><div className="val">None</div></div>
            <div className="box wide"><div className="lbl">Due today</div><div className="val">{fmt(price)}</div></div>
          </> :

        <>
            <div className="box"><div className="lbl">Surcharge ({Math.round(term.surcharge * 100)}%)</div><div className="val">{fmt(surchargeAmt)}</div></div>
            <div className="box"><div className="lbl">Total over {term.months}mo</div><div className="val">{fmt(total)}</div></div>
            <div className="box wide"><div className="lbl">Monthly payment</div><div className="val">{fmt(monthly)}/mo</div></div>
          </>}

      </div>
      <p className="small" style={{ marginTop: 10 }}>No down payment required on any plan. Late payments incur a 2% monthly penalty.</p>
      <a href="#brochure" className="btn btn-primary" style={{ width: '100%', justifyContent: 'center', marginTop: 12 }}>
        Lock this plan · Request the price list
      </a>
    </div>);

}

/* ---------- Payment plans ----------
   Layout mirrors the official price-list document: for each category, a
   full pricing table (every plot × every term), followed by that
   category's Payment Guide (Total / Monthly / Duration) for its base
   plot — same structure as "Regular Plot Payment Guide", "Regular Garden
   Plot Payment Guide", and "Family Vault Payment Guide" in the source doc. */
const CATEGORY_GUIDE_TIER_ID = {
  'Regular Plots': 'regular',
  'Garden Plots': 'garden-regular',
  'Family Vault': 'family-vault'
};

function PaymentPlans({ tiers }) {
  const { selectTier } = usePlanSelection();
  const categories = ['Regular Plots', 'Garden Plots', 'Family Vault'];
  const fiveYear = PLAN_TERMS[4];
  const cheapest = tiers.reduce((min, t) => t.price < min.price ? t : min, tiers[0] || { price: 0 });
  const lowestMonthly = cheapest.price * (1 + fiveYear.surcharge) / fiveYear.months;
  // Which term column gets the "lowest monthly" highlight + badge
  const BEST_VALUE_KEY = '5yr';

  const jumpToBrochure = (tierId) => (e) => {
    selectTier(tierId);
    // let the anchor scroll happen, selection is already applied via context
  };

  return (
    <section className="block" id="plans">
      <div className="wrap">
        <div className="section-head fade-up">
          <div>
            <span className="eyebrow">Payment Plans</span>
            <h2 style={{ marginTop: 18 }}>Own a piece of peace — for as little as {fmt(lowestMonthly)} a month.</h2>
          </div>
          <div className="side">
            No down payment on any plan. Choose spot cash, or spread the total over 1, 2, 3, or 5 years — the longer the term, the higher the surcharge, but the lower your monthly payment.
          </div>
        </div>

        {categories.map((cat) => {
          const group = tiers.filter((t) => t.category === cat);
          if (!group.length) return null;
          const guideTier = tiers.find((t) => t.id === CATEGORY_GUIDE_TIER_ID[cat]) || group[0];

          return (
            <div key={cat} style={{ marginBottom: 44 }}>
              <h3 className="cat-head fade-up">{cat}</h3>

              <table className="plan-table fade-up">
                <thead>
                  <tr>
                    <th>Plot Type</th>
                    {PLAN_TERMS.map((term) =>
                    <th key={term.key}>
                        {term.months ? term.label : 'Spot Cash'}
                        <div style={{ fontWeight: 400, textTransform: 'none', letterSpacing: 0, fontSize: 11, marginTop: 3, opacity: 0.8 }}>
                          {term.months ? `+${Math.round(term.surcharge * 100)}% surcharge` : 'no surcharge'}
                        </div>
                        {term.key === BEST_VALUE_KEY &&
                        <div style={{ marginTop: 5 }}>
                            <span style={{
                              display: 'inline-block', background: 'var(--gold)', color: '#1f1607',
                              borderRadius: 999, padding: '2px 9px', fontSize: 10, fontWeight: 700,
                              textTransform: 'uppercase', letterSpacing: '.04em'
                            }}>Lowest monthly</span>
                          </div>}
                      </th>
                    )}
                  </tr>
                </thead>
                <tbody>
                  {group.map((t) =>
                  <tr key={t.id}>
                      <td><strong>{t.name}</strong></td>
                      {PLAN_TERMS.map((term) => {
                      const total = t.price * (1 + term.surcharge);
                      const monthly = term.months ? total / term.months : null;
                      return (
                        <td key={term.key} className={term.key === BEST_VALUE_KEY ? 'highlight' : ''}>
                            <div>{fmt(total)}</div>
                            {monthly &&
                          <div style={{ fontSize: 12, color: 'var(--ink-2)', marginTop: 2, fontWeight: 400 }}>
                                {fmt(monthly)}/mo · {term.months}mo
                              </div>}
                          </td>);

                    })}
                    </tr>
                  )}
                </tbody>
              </table>
              <p className="small" style={{ marginTop: 10 }}>
                {guideTier.name} shown at {fmt(guideTier.price)} spot cash · no down payment on any plan.
              </p>

              <div style={{ marginTop: 14 }}>
                <a
                  href="#brochure"
                  className="btn btn-primary"
                  onClick={jumpToBrochure(guideTier.id)}
                  style={{
                    display: 'inline-flex',
                    alignItems: 'center',
                    gap: 8,
                    fontWeight: 700,
                    boxShadow: '0 6px 18px rgba(176,133,68,0.35)'
                  }}>
                  Get pricing for {cat} →
                </a>
              </div>
            </div>);

        })}

        <div className="fade-up" style={{ maxWidth: 420, marginBottom: 44 }}>
          <PaymentCalculator tiers={tiers} />
        </div>

        <div className="fade-up" style={{ maxWidth: 640 }}>
          <h3 className="display" style={{ fontSize: 24, marginBottom: 12 }}>What "pre-need" really saves you.</h3>
          <p style={{ color: 'var(--ink-2)', marginBottom: 18 }}>
            The average burial lot in Palawan has appreciated 8–12% per year for the past decade. Lock the price now, and your family pays nothing later.
          </p>
          <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>
            <a href="#brochure" className="btn btn-primary">Get a custom quote</a>
            <a href="tel:+639171234567" className="btn btn-ghost">Talk to a counselor</a>
          </div>
          <p className="small" style={{ marginTop: 18 }}>
            No down payment on any plan; a 2% monthly penalty applies to late payments.
          </p>
        </div>
      </div>
    </section>);

}

/* ---------- Mausoleum ---------- */
function fmtRange(low, high) {
  return high ? `${fmt(low)} – ${fmt(high)}` : `${fmt(low)}+`;
}

function Mausoleum() {
  return (
    <section className="block" id="mausoleum">
      <div className="wrap">
        <div className="section-head fade-up">
          <div>
            <span className="eyebrow">Mausoleum</span>
            <h2 style={{ marginTop: 18 }}>A permanent family legacy, built to your design.</h2>
          </div>
          <div className="side">
            Mausoleum plots are sold as land only unless bundled into a construction package. Bring your own architect, or work with ours — either way, the plot and design are yours to shape.
          </div>
        </div>

        <h3 className="cat-head fade-up">All-inclusive construction packages</h3>
        <div className="tiers">
          {MAUSOLEUM_PACKAGES.map((m, i) =>
          <div className="tier fade-up" key={m.size} style={{ '--stagger': i }}>
              <div className="tier-cine-card">
                <div className="cine-frame">
                  <div className="cine-zoom">
                    <Photo label={m.size} image={m.image} items={[{ label: m.size, caption: m.size }]} />
                  </div>
                  <div className="cine-vignette"></div>
                  <div className="cine-glow"></div>
                </div>
                <div>
                  <h3>{m.size}</h3>
                  <div className="desc">{m.desc}</div>
                </div>
                <div className="price-row">
                  <div>
                    <div className="from">{m.area}</div>
                    <div className="price" style={{ fontSize: 26 }}>{fmtRange(m.low, m.high)}</div>
                  </div>
                </div>
                <a href="#brochure" className="btn btn-primary">Request a quote →</a>
              </div>
            </div>
          )}
        </div>

        <div className="calc-card fade-up" style={{ marginTop: 36 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', flexWrap: 'wrap', gap: 20 }}>
            <div style={{ maxWidth: 480 }}>
              <span className="eyebrow">Two-Story Mausoleum</span>
              <h3 style={{ fontSize: 24, margin: '10px 0' }}>{TWO_STORY_MAUSOLEUM.area}</h3>
              <p style={{ color: 'var(--ink-2)' }}>{TWO_STORY_MAUSOLEUM.desc}</p>
              <p className="small" style={{ marginTop: 12 }}>Payment plans available with a surcharge over 1, 2, 3, or 5 years.</p>
            </div>
            <div style={{ textAlign: 'right' }}>
              <div className="calc-row-head"><label>Spot cash</label></div>
              <div className="price" style={{ fontSize: 30 }}>{fmtRange(TWO_STORY_MAUSOLEUM.low, TWO_STORY_MAUSOLEUM.high)}</div>
              <a href="#brochure" className="btn btn-gold" style={{ marginTop: 14 }}>Request a consultation →</a>
            </div>
          </div>
        </div>
      </div>
    </section>);

}

/* ---------- Investment ---------- */
function Investment() {
  return (
    <section className="block invest" id="invest">
      <div className="wrap">
        <div className="section-head fade-up">
          <div>
            <span className="eyebrow">Investment angle</span>
            <h2 style={{ marginTop: 18 }}>The only real estate that almost always goes up.</h2>
          </div>
          <div className="side">
            Memorial park lots in the Philippines have outpaced inflation every year since 2012. Many of our investor-clients buy 3–5 lots at a time for resale or family transfer.
          </div>
        </div>
        <div className="invest-grid">
          <div className="chart-card fade-up">
            <h4>Garden Lawn Lot · Price history</h4>
            <div className="chart-meta">Garden Lawn section · 2014 → 2026 · per lot, PHP</div>
            <AppreciationChart />
            <div className="invest-stats">
              <div className="invest-stat">
                <div className="num">+158%</div>
                <div className="lbl">Total appreciation, 2014–2026</div>
              </div>
              <div className="invest-stat">
                <div className="num">10.4%</div>
                <div className="lbl">Compounded annual growth</div>
              </div>
            </div>
          </div>
          <div className="fade-up">
            <h3 className="display" style={{ fontSize: 32, color: '#f6f1e8', marginBottom: 18, lineHeight: 1.15 }}>
              Three reasons resellers buy here.
            </h3>
            <ol style={{ padding: 0, listStyle: 'none', margin: 0 }}>
              {[
              ['Land scarcity.', 'Palawan zoning makes new memorial parks effectively impossible. Supply is fixed; demand grows with population.'],
              ['Transferable title.', 'Every lot comes with a Contract to Sell that can be assigned to any third party for a flat ₱2,500 admin fee.'],
              ['Resale assistance.', 'Our office connects sellers with at-need families directly — most lots resell in under 45 days.']].
              map(([head, body], i) =>
              <li key={i} style={{ display: 'flex', gap: 18, padding: '18px 0', borderBottom: '1px solid rgba(255,255,255,.1)' }}>
                  <div style={{
                  width: 36, height: 36, borderRadius: '50%',
                  background: 'var(--gold)', color: '#1f1607', flexShrink: 0,
                  display: 'grid', placeItems: 'center',
                  fontFamily: 'Newsreader, serif', fontSize: 18
                }}>{i + 1}</div>
                  <div>
                    <strong style={{ fontFamily: 'Newsreader, serif', fontSize: 20, color: '#f6f1e8', display: 'block', marginBottom: 4 }}>{head}</strong>
                    <span style={{ color: '#b6ad97', fontSize: 14 }}>{body}</span>
                  </div>
                </li>
              )}
            </ol>
            <a href="#brochure" className="btn btn-gold" style={{ marginTop: 28 }}>Get the investor pack</a>
          </div>
        </div>
      </div>
    </section>);

}

function AppreciationChart() {
  // 2014–2026 garden lot price (PHP, thousands)
  const data = [33, 36, 40, 45, 51, 57, 62, 66, 70, 74, 78, 82, 85];
  const years = ['’14', '’15', '’16', '’17', '’18', '’19', '’20', '’21', '’22', '’23', '’24', '’25', '’26'];
  const W = 460,H = 220,pad = { l: 36, r: 12, t: 10, b: 26 };
  const max = Math.max(...data) * 1.1;
  const xs = data.map((_, i) => pad.l + i / (data.length - 1) * (W - pad.l - pad.r));
  const ys = data.map((v) => H - pad.b - v / max * (H - pad.t - pad.b));
  const path = xs.map((x, i) => `${i === 0 ? 'M' : 'L'} ${x} ${ys[i]}`).join(' ');
  const area = `${path} L ${xs[xs.length - 1]} ${H - pad.b} L ${xs[0]} ${H - pad.b} Z`;

  return (
    <svg viewBox={`0 0 ${W} ${H}`} style={{ width: '100%', height: 'auto', display: 'block' }}>
      {/* gridlines */}
      {[0, 0.25, 0.5, 0.75, 1].map((p, i) =>
      <line key={i}
      x1={pad.l} x2={W - pad.r}
      y1={pad.t + p * (H - pad.t - pad.b)} y2={pad.t + p * (H - pad.t - pad.b)}
      stroke="rgba(255,255,255,.08)" strokeWidth="1" />
      )}
      {/* y labels */}
      {[max, max * 0.75, max * 0.5, max * 0.25, 0].map((v, i) =>
      <text key={i}
      x={pad.l - 8} y={pad.t + i / 4 * (H - pad.t - pad.b) + 4}
      textAnchor="end" fill="#8c8473" fontSize="9" fontFamily="JetBrains Mono, monospace">
          {Math.round(v)}k
        </text>
      )}
      {/* area */}
      <defs>
        <linearGradient id="g1" x1="0" x2="0" y1="0" y2="1">
          <stop offset="0%" stopColor="#b08544" stopOpacity="0.4" />
          <stop offset="100%" stopColor="#b08544" stopOpacity="0" />
        </linearGradient>
      </defs>
      <path d={area} fill="url(#g1)" />
      <path d={path} stroke="#b08544" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round" />
      {/* points */}
      {xs.map((x, i) =>
      <circle key={i} cx={x} cy={ys[i]} r="2.5" fill="#b08544" />
      )}
      {/* x labels */}
      {years.map((y, i) => i % 2 === 0 &&
      <text key={i} x={xs[i]} y={H - 8} textAnchor="middle" fill="#8c8473" fontSize="9" fontFamily="JetBrains Mono, monospace">{y}</text>
      )}
    </svg>);

}

/* ---------- Pre-need vs At-need ---------- */
function PrePostNeed() {
  return (
    <section className="block">
      <div className="wrap">
        <div className="section-head fade-up">
          <div>
            <span className="eyebrow">Pre-need vs. At-need</span>
            <h2 style={{ marginTop: 18 }}>The single best decision you can make for your family.</h2>
          </div>
          <div className="side">
            Pre-need (planning ahead) typically saves families 40–60% versus at-need pricing — and removes the financial burden during the hardest week of their lives.
          </div>
        </div>
        <div className="compare-grid">
          <div className="compare-card recommended fade-up">
            <div className="tag">Recommended · Plan ahead</div>
            <h3>Pre-Need Plan</h3>
            <div className="save">Save up to 57%</div>
            <div className="price" style={{ fontFamily: 'Newsreader, serif', fontSize: 44 }}>
              ₱30,000<span style={{ fontSize: 16, color: 'var(--ink-2)' }}> /lot</span>
            </div>
            <ul>
              <li><span className="check">✓</span> Lock today's price — protected from yearly increases</li>
              <li><span className="check">✓</span> 0–10% down payment, terms up to 60 months</li>
              <li><span className="check">✓</span> Transferable to any family member or third party</li>
              <li><span className="check">✓</span> Full refund within 30 days of reservation</li>
              <li><span className="check">✓</span> Choice of any open section, including premium blocks</li>
            </ul>
          </div>
          <div className="compare-card fade-up">
            <div className="tag">When the time comes</div>
            <h3>At-Need Pricing</h3>
            <div style={{ height: 28 }}></div>
            <div className="price" style={{ fontFamily: 'Newsreader, serif', fontSize: 44 }}>
              ₱35,000<span style={{ fontSize: 16, color: 'var(--ink-2)' }}> /lot, est. 2030</span>
            </div>
            <ul>
              <li>Same lot, future market price</li>
              <li>Full payment due within 48 hours</li>
              <li>Limited to remaining available sections</li>
              <li>Often during an emotionally difficult week</li>
              <li>Premium blocks usually unavailable</li>
            </ul>
          </div>
        </div>
      </div>
    </section>);

}

/* ---------- Gallery ---------- */
function Gallery() {
  const tiles = [
  'main entrance arch — golden hour, 16:10',
  'lake of remembrance — reflection, 4:5',
  'chapel of light — interior, 1:1',
  'family estate pathway — 1:1',
  'mausoleum row — heritage, 16:10',
  'aerial — full park map, 16:10'];
  const scenes = ['entrance', 'lake', 'chapel-facade', 'family-pathway', 'mausoleum-row', 'aerial-map'];

  return (
    <section className="block" id="gallery">
      <div className="wrap">
        <div className="section-head fade-up">
          <div>
            <span className="eyebrow">The park</span>
            <h2 style={{ marginTop: 18 }}>4.9 hectares of quiet gardens in Taytay, Palawan.</h2>
          </div>
          <div className="side">
            Browse our lawn blocks A to H and the Mausoleum, then book a free site visit to see them in person. We're open 7 days a week, with pickup included within Palawan.
          </div>
        </div>
        <div className="gallery-grid">
          {tiles.map((t, i) =>
          <Photo
            key={i}
            label={t}
            scene={scenes[i]}
            className="fade-up"
            style={{ '--stagger': i }}
            items={tiles.map((label) => ({ label, caption: 'Golden Harmonic · the park' }))}
            index={i} />

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

}

/* ---------- About ---------- */
function About() {
  return (
    <section className="block" id="about">
      <div className="wrap">
        <div className="about-grid">
          <Photo aspect="aspect-tall" className="fade-up" label="founders portrait — 1998, sepia 3:4"
            items={[{ label: 'founders portrait — 1998, sepia 3:4', caption: 'The Mañalac family, 1998' }]} />
          <div className="fade-up">
            <span className="eyebrow">About Golden Harmonic</span>
            <h2 style={{ margin: '18px 0 24px', fontSize: 'clamp(30px, 3.5vw, 44px)' }}>
              A family park, built by a Palawan family, for Palawan families.
            </h2>
            <p>
              Golden Harmonic was founded in 1998 by the Mañalac family on a single hectare of lakeside land in Palawan. Twenty-eight years and twelve thousand families later, we still answer the phone ourselves.
            </p>
            <p>
              Every plot we sell contributes to a perpetual care fund — independently audited each year — which guarantees that the gardens you walk through today will look exactly the same in fifty years.
            </p>
            <p>
              We welcome families of every faith. Dedicated sections are available for Catholic, Christian, Muslim, and Chinese-Filipino burial customs.
            </p>
            <div className="faiths">
              {['Catholic section', 'Christian section', 'Muslim section', 'Chinese-Filipino section', 'Non-denominational'].map((f) =>
              <span className="faith-chip" key={f}>{f}</span>
              )}
            </div>
          </div>
        </div>
      </div>
    </section>);

}

/* ---------- FAQ ---------- */
function FAQ() {
  const qs = [
  ['Can I transfer my lot to someone else?', 'Yes. Every plot is sold via a Contract to Sell that is freely transferable. We charge a flat ₱2,500 administrative fee to process the transfer.'],
  ['What does "perpetual care" actually mean?', 'A portion of every lot sale is deposited into an independently-audited trust fund. The fund earns interest in perpetuity and pays for landscaping, security, drainage, and structural maintenance — forever, regardless of ownership changes.'],
  ['What if I move to another province?', 'Your contract is location-independent. You can transfer your reservation to another family member, sell it through our office, or have the lot redeemed for a credit toward funeral services at any partner provider.'],
  ['Is there a refund if I change my mind?', 'Yes. Full refund within 30 days of reservation, less a 5% processing fee. After 30 days, paid principal can be converted to credit, applied to another lot, or sold via our resale program.'],
  ['Do you accept all religions?', 'Yes. We have dedicated sections for Catholic, Christian, Muslim, and Chinese-Filipino burial customs, plus a non-denominational area. Our chapel can be used for any rite.'],
  ['Can I see the park before buying?', 'Of course — and we encourage it. We offer free site visits 7 days a week, with pickup included within Palawan.'],
  ['How does the investment / resale program work?', 'Our resale office matches sellers with at-need families. There is no upfront cost — we collect a 6% commission only when the lot resells. Most lots clear in under 45 days.'],
  ['Is a down payment required?', 'No. None of our payment plans require a down payment — start with spot cash, or any of the 1, 2, 3, or 5-year installment terms.'],
  ['What happens if I miss a payment?', 'A 2% monthly penalty applies to late payments on installment plans. Contact us as early as possible if you expect to be late — we can usually work out a short extension.'],
  ['Can I pay off my plan early?', 'Yes — early payment discounts may be available. Ask your counselor for current terms when you\'re ready to settle the balance.'],
  ['Do I have to buy a mausoleum construction package?', 'No. Mausoleum plots are sold as land only unless you choose one of our all-inclusive construction packages. You\'re free to bring your own architect or contractor, or work with ours.']];

  return (
    <section className="block" id="faq">
      <div className="wrap">
        <div className="section-head fade-up">
          <div>
            <span className="eyebrow">Common questions</span>
            <h2 style={{ marginTop: 18 }}>Everything families ask before reserving.</h2>
          </div>
          <div className="side">
            Don't see your question? Call us at <a href="tel:+639171234567" style={{ color: 'var(--accent)', fontWeight: 600 }}>+63 917 123 4567</a> or request the full price list — it includes a 20-page Q&A booklet.
          </div>
        </div>
        <div className="faq-grid fade-up">
          {qs.map(([q, a], i) =>
          <details className="faq-item" key={i}>
              <summary>
                <span>{q}</span>
                <span className="toggle">+</span>
              </summary>
              <div className="answer">{a}</div>
            </details>
          )}
        </div>
      </div>
    </section>);

}

/* ---------- Brochure / contact form ---------- */
function Brochure({ tiers = [] }) {
  const [sent, setSent]     = useState(false);
  const [saving, setSaving] = useState(false);
  const [error, setError]   = useState('');
  const [form, setForm]     = useState({
    firstName: '', lastName: '', email: '', phone: '',
    interest: 'Pre-need (planning ahead)', planId: ''
  });

  const set = (k) => (e) => setForm(f => ({ ...f, [k]: e.target.value }));
  const chosenPlan = tiers.find(t => t.id === form.planId);
  const planImageFile = (planId) => `plot-${planId}.png`;
  const submit = async (e) => {
    e.preventDefault();
    setSaving(true);
    setError('');
    try {
      if (window.db) {
        const ts   = firebase.firestore.FieldValue.serverTimestamp();
        const base = {
          firstName:         form.firstName,
          lastName:          form.lastName,
          fullName:          `${form.firstName} ${form.lastName}`,
          email:             form.email,
          phone:             form.phone,
          interest:          form.interest,
          source:            'website',
          createdAt:         ts,
          ...(chosenPlan ? { planId: chosenPlan.id, planName: chosenPlan.name, planPrice: chosenPlan.price } : {})
        };
        // 1. Save inquiry
        const inquiryRef = await window.db.collection('inquiries').add({
          ...base,
          type:   'brochure_request',
          status: 'new'
        });
        // 2. Save customer / lead
        await window.db.collection('customers').add({
          ...base,
          status:     'lead',
          inquiryIds: [inquiryRef.id],
          ...(chosenPlan ? {
            interestedPlanId:    chosenPlan.id,
            interestedPlanName:  chosenPlan.name,
            interestedPlanPrice: chosenPlan.price
          } : {})
        });
      }

      // 3. Send the customer a confirmation email (best-effort — if EmailJS
      // isn't configured yet, this just quietly does nothing so the form
      // still works while you're setting it up).
      if (window.emailjs && window.EMAILJS_CONFIG && window.EMAILJS_CONFIG.PUBLIC_KEY !== 'YOUR_PUBLIC_KEY') {
        emailjs.send(window.EMAILJS_CONFIG.SERVICE_ID, window.EMAILJS_CONFIG.TEMPLATE_ID, {
          to_email:   form.email,
          first_name: form.firstName,
          last_name:  form.lastName,
          interest:   form.interest,
          phone:      form.phone,
          plan_name:  chosenPlan ? chosenPlan.name : 'Not chosen yet',
          plan_price: chosenPlan ? fmt(chosenPlan.price) : '—',
          plan_image: chosenPlan ? `${window.location.origin}/${planImageFile(chosenPlan.id)}` : '',
          plot_message: chosenPlan ? (PLOT_MESSAGES[chosenPlan.id] || '') : ''
        }).catch(err => console.error('[GH] Email send error:', err));
      }

      setSent(true);
    } catch (err) {
      console.error('[GH] Save error:', err);
      setError('Could not send your request. Please call us at +63 917 123 4567.');
    } finally {
      setSaving(false);
    }
  };

  return (
    <section className="brochure" id="brochure">
      <div className="wrap brochure-grid">
        <div className="fade-up">
          <span className="eyebrow">Get the full price list</span>
          <h2 style={{ marginTop: 18 }}>The complete 24-page brochure — yours in under a minute.</h2>
          <p className="lede">
            Send us your details and we'll email the full price list with every section, every plan, and current promos. No salesperson will call unless you ask.
          </p>
          <div className="perks">
            <div><span className="ico">1</span><span><strong>Full price tables</strong> for every garden section.</span></div>
            <div><span className="ico">2</span><span><strong>All payment plan options</strong> with sample computations.</span></div>
            <div><span className="ico">3</span><span><strong>20-page Q&A booklet</strong> covering transfers, refunds, and resale.</span></div>
            <div><span className="ico">4</span><span><strong>Free 7-day reservation hold</strong> on any plot you choose.</span></div>
          </div>
        </div>
        <form className="form-card fade-up" onSubmit={submit}>
          {sent ? (
            <div style={{ padding: '32px 0', textAlign: 'center' }}>
              <div style={{
                width: 64, height: 64, borderRadius: '50%', background: 'var(--accent)',
                color: 'var(--accent-ink)', margin: '0 auto 20px',
                display: 'grid', placeItems: 'center', fontSize: 30
              }}>✓</div>
              <h3 style={{ marginBottom: 8 }}>Brochure on its way.</h3>
              <p style={{ color: 'var(--ink-2)', fontSize: 14, marginBottom: 24 }}>
                Check your inbox in the next 5 minutes. We've also reserved a 7-day hold on any plot you're interested in — just reply to the email to claim it.
              </p>

              <div style={{
                textAlign: 'left', border: '1px solid var(--line)', borderRadius: 12,
                overflow: 'hidden', background: 'var(--card)'
              }}>
                {chosenPlan && (
                  <div className="ph" style={{ aspectRatio: '16/7', margin: 0, borderRadius: 0 }}>
                    <Scene variant={chosenPlan.id} />
                  </div>
                )}
                <div style={{ padding: 18 }}>
                  <div className="label" style={{ fontSize: 11, textTransform: 'uppercase', letterSpacing: '.05em', color: 'var(--ink-3)', marginBottom: 10 }}>
                    Your summary
                  </div>
                  <div style={{ display: 'grid', gap: 8, fontSize: 14 }}>
                    <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                      <span style={{ color: 'var(--ink-2)' }}>Plot</span>
                      <strong>{chosenPlan ? chosenPlan.name : 'Not chosen yet'}</strong>
                    </div>
                    {chosenPlan && (
                      <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                        <span style={{ color: 'var(--ink-2)' }}>Spot cash price</span>
                        <strong>{fmt(chosenPlan.price)}</strong>
                      </div>
                    )}
                    <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                      <span style={{ color: 'var(--ink-2)' }}>Interested in</span>
                      <strong>{form.interest}</strong>
                    </div>
                    <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                      <span style={{ color: 'var(--ink-2)' }}>Name</span>
                      <strong>{form.firstName} {form.lastName}</strong>
                    </div>
                    <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                      <span style={{ color: 'var(--ink-2)' }}>Email</span>
                      <strong>{form.email}</strong>
                    </div>
                    <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                      <span style={{ color: 'var(--ink-2)' }}>Mobile</span>
                      <strong>{form.phone}</strong>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          ) : (
            <>
              <h3>Request the price list</h3>
              <div className="form-sub">Free · No payment required · No salesperson unless you ask</div>
              <div className="field-row">
                <div className="field">
                  <label>First name</label>
                  <input type="text" required placeholder="Maria"
                    value={form.firstName} onChange={set('firstName')} />
                </div>
                <div className="field">
                  <label>Last name</label>
                  <input type="text" required placeholder="Santos"
                    value={form.lastName} onChange={set('lastName')} />
                </div>
              </div>
              <div className="field">
                <label>Email</label>
                <input type="email" required placeholder="maria@example.com"
                  value={form.email} onChange={set('email')} />
              </div>
              <div className="field">
                <label>Mobile number</label>
                <input type="tel" required placeholder="+63 917 ..."
                  value={form.phone} onChange={set('phone')} />
              </div>
              <div className="field-row">
                <div className="field">
                  <label>Which plot interests you?</label>
                  <select value={form.planId} onChange={set('planId')}>
                    <option value="">Not sure yet — send general info</option>
                    {tiers.map(t => (
                      <option key={t.id} value={t.id}>{t.name} — {fmt(t.price)}</option>
                    ))}
                  </select>
                </div>
                <div className="field">
                  <label>I'm interested in</label>
                  <select value={form.interest} onChange={set('interest')}>
                    <option>Pre-need (planning ahead)</option>
                    <option>At-need (immediate)</option>
                    <option>Investment / resale</option>
                    <option>Just exploring</option>
                  </select>
                </div>
              </div>
              {error && (
                <p style={{ color: 'var(--terracotta)', fontSize: 13, marginBottom: 8, fontWeight: 500 }}>
                  ⚠ {error}
                </p>
              )}
              <button className="btn btn-primary" type="submit" disabled={saving}
                style={{ opacity: saving ? 0.7 : 1 }}>
                {saving ? 'Sending…' : 'Send me the price list →'}
              </button>
              <p className="small" style={{ marginTop: 14, textAlign: 'center' }}>
                We use your details only to answer your request and never sell them. See our <a href="/privacy.html" target="_blank" rel="noopener">Privacy Policy</a>.
              </p>
            </>
          )}
        </form>
      </div>
    </section>
  );
}

/* ---------- Footer ---------- */
/* ---------- Customer Portal (balance / payment history / submit reference) ----------
   Accounts are created by staff in admin.html (Customer detail → "Create portal login"),
   which sets customer.authUid to the Firebase Auth UID. We look up the matching
   customer doc by that field — there is no self-signup here. */
function customerPlanBalance(c) {
  if (!c || !c.plan) return null;
  const paid = (c.payments || []).reduce((s, p) => s + Number(p.amount || 0), 0);
  // plan.price is the base cash price with no installment surcharge —
  // plan.totalPrice (price + term surcharge, e.g. +20% on a 3-year plan)
  // is what's actually owed. Fall back to price only for older records
  // saved before totalPrice existed.
  const price = Number(c.plan.totalPrice ?? c.plan.price) || 0;
  const monthly = Number(c.plan.monthlyAmount) || 0;
  const termMonths = Number(c.plan.termMonths) || 0;
  const paymentsMade = monthly ? Math.min(termMonths || Infinity, Math.floor(paid / monthly)) : 0;
  return {
    price, paid, balance: Math.max(0, price - paid),
    pct: price ? Math.min(100, (paid / price) * 100) : 0,
    monthly, termMonths, paymentsMade
  };
}

function PortalLogin({ onForgot }) {
  const [email, setEmail] = useState('');
  const [pass, setPass] = useState('');
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState('');

  const submit = async (e) => {
    e.preventDefault();
    setBusy(true); setErr('');
    try {
      await window.auth.signInWithEmailAndPassword(email.trim(), pass);
    } catch (ex) {
      console.error('[GH portal login]', ex.code, ex.message, ex);
      const map = {
        'auth/invalid-email': 'That email address doesn’t look right.',
        'auth/user-not-found': 'We couldn’t find a portal login for that email. Ask our staff to set one up for you.',
        'auth/wrong-password': 'That password isn’t right — try again or reset it below.',
        'auth/invalid-credential': 'Email or password isn’t right — try again or reset it below.',
        'auth/too-many-requests': 'Too many attempts — please wait a bit and try again.'
      };
      setErr(map[ex.code] || ex.message || 'Could not log in.');
    } finally { setBusy(false); }
  };

  return (
    <form onSubmit={submit} className="gh-portal-form">
      <label htmlFor="gh-login-email">Email</label>
      <input id="gh-login-email" type="email" required value={email} onChange={e => setEmail(e.target.value)} placeholder="name@example.com" />
      <label htmlFor="gh-login-password">Password</label>
      <input id="gh-login-password" type="password" required value={pass} onChange={e => setPass(e.target.value)} placeholder="••••••••" />
      {err && <div className="gh-portal-err" role="alert">{err}</div>}
      <button type="submit" className="gh-portal-btn-primary" disabled={busy}>{busy ? 'Logging in…' : 'Log in'}</button>
      <button type="button" className="gh-portal-link-btn" onClick={() => onForgot(email)}>Forgot your password?</button>
    </form>
  );
}

function PortalForgot({ initialEmail, onBack }) {
  const [email, setEmail] = useState(initialEmail || '');
  const [busy, setBusy] = useState(false);
  const [sent, setSent] = useState(false);
  const [err, setErr] = useState('');

  const submit = async (e) => {
    e.preventDefault();
    setBusy(true); setErr('');
    try {
      await window.auth.sendPasswordResetEmail(email.trim());
      setSent(true);
    } catch (ex) {
      setErr(ex.message || 'Could not send the reset email.');
    } finally { setBusy(false); }
  };

  if (sent) {
    return (
      <div className="gh-portal-form">
        <p>Check your inbox — we sent a password reset link to <strong>{email}</strong>.</p>
        <button type="button" className="gh-portal-link-btn" onClick={onBack}>Back to login</button>
      </div>
    );
  }

  return (
    <form onSubmit={submit} className="gh-portal-form">
      <label htmlFor="gh-forgot-email">Email address</label>
      <input id="gh-forgot-email" type="email" required value={email} onChange={e => setEmail(e.target.value)} />
      {err && <div className="gh-portal-err" role="alert">{err}</div>}
      <button type="submit" className="gh-portal-btn-primary" disabled={busy}>{busy ? 'Sending…' : 'Send reset link'}</button>
      <button type="button" className="gh-portal-link-btn" onClick={onBack}>Back to login</button>
    </form>
  );
}

function PortalDashboard({ customer, onLogout }) {
  const bal = customerPlanBalance(customer);
  const confirmedRows = (customer.payments || []).map(p => ({ ...p, _kind: 'confirmed' }));
  const pendingRows = (customer.paymentSubmissions || []).map(p => ({ ...p, _kind: 'submission' }));
  const history = [...confirmedRows, ...pendingRows].sort((a, b) => (a.date < b.date ? 1 : -1));

  // Payment method tab: 'online' (PayMongo) | 'manual' (Reference)
  const [payTab, setPayTab] = useState('online');

  // Online PayMongo state
  const [onlineType, setOnlineType] = useState('monthly'); // 'monthly' | 'full' | 'custom'
  const [customOnlineAmt, setCustomOnlineAmt] = useState('');
  const [onlineBusy, setOnlineBusy] = useState(false);
  const [onlineErr, setOnlineErr] = useState('');

  // Manual submission state
  const [form, setForm] = useState({ method: 'gcash', referenceNumber: '', amount: '', date: new Date().toISOString().slice(0, 10), mobileNumber: '', note: '' });
  const [busy, setBusy] = useState(false);
  const [toast, setToast] = useState('');

  // Online amounts & fee calculation
  const monthlyDue = Number(customer.plan?.monthlyAmount) || 0;
  const rawTargetAmount = onlineType === 'monthly'
    ? monthlyDue
    : onlineType === 'full'
    ? Math.max(0, bal.balance)
    : Number(customOnlineAmt) || 0;

  const convenienceFee = Math.round(rawTargetAmount * 0.025 * 100) / 100;
  const totalToPay = rawTargetAmount + convenienceFee;

  const handlePayMongoCheckout = async () => {
    if (rawTargetAmount < 50) {
      setOnlineErr('Please enter an amount of at least ₱50.');
      return;
    }
    setOnlineBusy(true);
    setOnlineErr('');
    try {
      const res = await fetch('/api/paymongo-checkout', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          type: 'portal_installment',
          amount: rawTargetAmount,
          description: `Plan Installment: ${customer.plan?.tierName || 'Memorial Plot'} (${customer.firstName || customer.fullName || 'Customer'})`,
          customerId: customer._id,
          customerEmail: customer.email || '',
          customerName: customer.fullName || `${customer.firstName || ''} ${customer.lastName || ''}`.trim(),
          customerPhone: customer.phone || ''
        })
      });
      const data = await res.json().catch(() => ({}));
      if (res.ok && data.checkoutUrl) {
        window.location.href = data.checkoutUrl;
      } else {
        if (res.status === 405) {
          setOnlineErr('The local preview server (Live Server) cannot process API payments. Please run via "vercel dev" or test on the deployed site.');
        } else {
          setOnlineErr(data.error || 'Failed to start PayMongo checkout (status ' + res.status + '). Please try again.');
        }
      }
    } catch (ex) {
      setOnlineErr('Connection error. Please check your internet connection.');
    } finally {
      setOnlineBusy(false);
    }
  };

  const submitReference = async (e) => {
    e.preventDefault();
    if (form.method !== 'cash' && !form.referenceNumber.trim()) return; // cash payments may not have a reference
    if (!form.amount || Number(form.amount) <= 0) return;
    setBusy(true);
    try {
      await window.db.collection('customers').doc(customer._id).update({
        paymentSubmissions: firebase.firestore.FieldValue.arrayUnion({
          referenceNumber: form.referenceNumber.trim(),
          amount: Number(form.amount),
          date: form.date,
          method: form.method === 'bank' ? 'Bank Transfer' : form.method === 'cash' ? 'Cash' : 'GCash',
          mobileNumber: form.mobileNumber.trim(),
          note: form.note.trim(),
          status: 'pending',
          submittedAt: Date.now()
        })
      });
      setForm({ method: form.method, referenceNumber: '', amount: '', date: new Date().toISOString().slice(0, 10), mobileNumber: '', note: '' });
      setToast('Submitted — our staff will confirm this against the transaction.');
      setTimeout(() => setToast(''), 6000);
    } catch (ex) {
      setToast(ex.message || 'Could not submit — please try again.');
    } finally { setBusy(false); }
  };

  return (
    <div>
      <div className="gh-portal-topbar">
        <span>Hello, {customer.firstName || customer.fullName || 'there'}</span>
        <button className="gh-portal-link-btn" style={{ marginTop: 0 }} onClick={onLogout}>Log out</button>
      </div>

      {!customer.plan ? (
        <div className="gh-portal-empty">No payment plan has been set up on your account yet. Please contact our staff.</div>
      ) : (
        <React.Fragment>
          <div className="gh-portal-hero">
            <div className="gh-portal-hero-label">Remaining balance</div>
            <div className="gh-portal-hero-amount">{fmt(bal.balance)}</div>
            <div className="gh-portal-hero-meta">{customer.plan.tierName}{customer.plan.termKey ? ' · ' + customer.plan.termKey : ''}</div>

            <div className="gh-portal-progress" role="progressbar" aria-valuenow={Math.round(bal.pct)} aria-valuemin={0} aria-valuemax={100}
              aria-label={`${Math.round(bal.pct)}% of plan paid off`}>
              <div className="gh-portal-progress-track">
                <div className="gh-portal-progress-fill" style={{ width: `${Math.max(2, bal.pct)}%` }} />
              </div>
              <div className="gh-portal-progress-caption">
                <span>{fmt(bal.paid)} of {fmt(bal.price)} paid</span>
                <span>{Math.round(bal.pct)}%{bal.termMonths ? ` · payment ${Math.min(bal.paymentsMade + 1, bal.termMonths)} of ${bal.termMonths}` : ''}</span>
              </div>
            </div>
          </div>

          <div className="gh-portal-stats">
            <div><div className="k">Total plan price</div><div className="v">{fmt(bal.price)}</div></div>
            <div><div className="k">Amount paid</div><div className="v">{fmt(bal.paid)}</div></div>
            <div><div className="k">Monthly due</div><div className="v">{fmt(customer.plan.monthlyAmount)}</div></div>
            <div><div className="k">Progress</div><div className="v">{Math.round(bal.pct)}%</div></div>
          </div>
        </React.Fragment>
      )}

      {/* Payment Options Header / Tabs */}
      <div className="gh-portal-section">
        <h4>Make a payment</h4>
        <div style={{ display: 'flex', gap: 8, marginBottom: 16 }}>
          <button
            type="button"
            onClick={() => setPayTab('online')}
            style={{
              flex: 1, padding: '10px 14px', borderRadius: 10, fontSize: 13.5, fontWeight: 700,
              cursor: 'pointer', fontFamily: 'inherit',
              border: payTab === 'online' ? '2px solid var(--accent)' : '1px solid var(--line)',
              background: payTab === 'online' ? 'var(--accent)' : 'var(--bg)',
              color: payTab === 'online' ? 'var(--accent-ink)' : 'var(--ink)'
            }}>
            ⚡ Pay Online (PayMongo)
          </button>
          <button
            type="button"
            onClick={() => setPayTab('manual')}
            style={{
              flex: 1, padding: '10px 14px', borderRadius: 10, fontSize: 13.5, fontWeight: 700,
              cursor: 'pointer', fontFamily: 'inherit',
              border: payTab === 'manual' ? '2px solid var(--accent)' : '1px solid var(--line)',
              background: payTab === 'manual' ? 'var(--accent)' : 'var(--bg)',
              color: payTab === 'manual' ? 'var(--accent-ink)' : 'var(--ink)'
            }}>
            📝 Submit Reference
          </button>
        </div>

        {payTab === 'online' ? (
          <div style={{ background: 'var(--bg)', border: '1px solid var(--line)', borderRadius: 12, padding: 16 }}>
            <p className="gh-portal-help" style={{ marginBottom: 12 }}>
              Pay directly using <b>GCash, Maya, Cards (Visa/Mastercard), GrabPay, or BillEase</b>. Your payment is verified automatically and credited to your balance instantly.
            </p>

            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(130px, 1fr))', gap: 8, marginBottom: 14 }}>
              {monthlyDue > 0 ? (
                <button
                  type="button"
                  onClick={() => { setOnlineType('monthly'); setCustomOnlineAmt(''); }}
                  style={{
                    padding: '10px 12px', borderRadius: 10, textAlign: 'left', cursor: 'pointer', fontFamily: 'inherit',
                    border: onlineType === 'monthly' ? '2px solid var(--accent)' : '1px solid var(--line)',
                    background: onlineType === 'monthly' ? '#eaf2ec' : 'var(--card)',
                    color: 'var(--ink)'
                  }}>
                  <div style={{ fontSize: 11, color: 'var(--ink-2)', fontWeight: 600 }}>MONTHLY DUE</div>
                  <div style={{ fontSize: 14, fontWeight: 700, marginTop: 2 }}>{fmt(monthlyDue)}</div>
                </button>
              ) : null}

              {bal.balance > 0 ? (
                <button
                  type="button"
                  onClick={() => { setOnlineType('full'); setCustomOnlineAmt(''); }}
                  style={{
                    padding: '10px 12px', borderRadius: 10, textAlign: 'left', cursor: 'pointer', fontFamily: 'inherit',
                    border: onlineType === 'full' ? '2px solid var(--accent)' : '1px solid var(--line)',
                    background: onlineType === 'full' ? '#eaf2ec' : 'var(--card)',
                    color: 'var(--ink)'
                  }}>
                  <div style={{ fontSize: 11, color: 'var(--ink-2)', fontWeight: 600 }}>FULL BALANCE</div>
                  <div style={{ fontSize: 14, fontWeight: 700, marginTop: 2 }}>{fmt(bal.balance)}</div>
                </button>
              ) : null}

              <button
                type="button"
                onClick={() => setOnlineType('custom')}
                style={{
                  padding: '10px 12px', borderRadius: 10, textAlign: 'left', cursor: 'pointer', fontFamily: 'inherit',
                  border: onlineType === 'custom' ? '2px solid var(--accent)' : '1px solid var(--line)',
                  background: onlineType === 'custom' ? '#eaf2ec' : 'var(--card)',
                  color: 'var(--ink)'
                }}>
                <div style={{ fontSize: 11, color: 'var(--ink-2)', fontWeight: 600 }}>OTHER AMOUNT</div>
                <div style={{ fontSize: 14, fontWeight: 700, marginTop: 2 }}>Custom ₱</div>
              </button>
            </div>

            {onlineType === 'custom' ? (
              <div style={{ marginBottom: 14 }}>
                <label htmlFor="gh-custom-online-amt" style={{ display: 'block', fontSize: 12.5, fontWeight: 600, marginBottom: 5 }}>Enter amount to pay (₱)</label>
                <input
                  id="gh-custom-online-amt"
                  type="number"
                  min="50"
                  max={bal.balance || 1000000}
                  placeholder="e.g. 2500"
                  value={customOnlineAmt}
                  onChange={(e) => setCustomOnlineAmt(e.target.value)}
                  style={{ width: '100%', padding: '10px 12px', borderRadius: 8, border: '1px solid var(--line)', fontSize: 14, boxSizing: 'border-box' }}
                />
              </div>
            ) : null}

            {rawTargetAmount > 0 ? (
              <div style={{ background: 'var(--card)', border: '1px solid var(--line)', borderRadius: 8, padding: '12px 14px', marginBottom: 14, fontSize: 12.5 }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 4 }}>
                  <span style={{ color: 'var(--ink-2)' }}>Credited toward plan:</span>
                  <span style={{ fontWeight: 600 }}>{fmt(rawTargetAmount)}</span>
                </div>
                <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 4 }}>
                  <span style={{ color: 'var(--ink-2)' }}>Processing fee (2.5%):</span>
                  <span style={{ color: 'var(--ink-2)' }}>{fmt(convenienceFee)}</span>
                </div>
                <div style={{ display: 'flex', justifyContent: 'space-between', borderTop: '1px dashed var(--line)', paddingTop: 6, fontWeight: 700, fontSize: 13.5, color: 'var(--ink)' }}>
                  <span>Total at checkout:</span>
                  <span style={{ color: 'var(--accent)' }}>{fmt(totalToPay)}</span>
                </div>
              </div>
            ) : null}

            <button
              type="button"
              disabled={onlineBusy || rawTargetAmount < 50}
              onClick={handlePayMongoCheckout}
              className="gh-portal-btn-primary"
              style={{ marginTop: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8 }}>
              {onlineBusy ? 'Connecting to PayMongo…' : `Pay ${rawTargetAmount > 0 ? fmt(totalToPay) : ''} Online →`}
            </button>

            {onlineErr ? <div className="gh-portal-err">{onlineErr}</div> : null}
            <div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center', gap: 10, marginTop: 12, opacity: 0.8, fontSize: 11, color: 'var(--ink-2)' }}>
              <span>📱 GCash</span> &bull; <span>💚 Maya</span> &bull; <span>💳 Visa/Mastercard</span> &bull; <span>⚡ BillEase</span>
            </div>
          </div>
        ) : (
          <div>
            <p className="gh-portal-help">
              {form.method === 'bank'
                ? "Send your payment to our business bank account, then submit the transfer reference below. Our staff will confirm it against the transaction and update your balance."
                : form.method === 'cash'
                ? "If you've paid in cash at our office, let us know here so our staff can confirm it and update your balance."
                : "Pay via our GCash for Business QR code, then send us the reference number. Our staff will confirm it against the transaction and update your balance."}
            </p>
            <div style={{ display: 'flex', gap: 8, marginBottom: 16 }}>
              {[
                { key: 'gcash', label: 'GCash' },
                { key: 'bank', label: 'Bank Transfer' },
                { key: 'cash', label: 'Cash' }
              ].map((opt) => (
                <button
                  key={opt.key}
                  type="button"
                  onClick={() => setForm(f => ({ ...f, method: opt.key }))}
                  style={{
                    flex: 1, padding: '10px 12px', borderRadius: 10, fontSize: 13.5, fontWeight: 600,
                    cursor: 'pointer', fontFamily: 'inherit',
                    border: form.method === opt.key ? '1px solid var(--accent)' : '1px solid var(--line)',
                    background: form.method === opt.key ? 'var(--accent)' : 'var(--bg)',
                    color: form.method === opt.key ? 'var(--accent-ink)' : 'var(--ink)'
                  }}>
                  {opt.label}
                </button>
              ))}
            </div>
            <form onSubmit={submitReference} className="gh-portal-form-grid">
              <div>
                <label htmlFor="gh-pay-ref">
                  {form.method === 'bank' ? 'Bank transfer reference no.' : form.method === 'cash' ? 'Receipt/OR number (if any)' : 'GCash reference number'}
                </label>
                <input
                  id="gh-pay-ref" type="text" required={form.method !== 'cash'}
                  value={form.referenceNumber} onChange={e => setForm(f => ({ ...f, referenceNumber: e.target.value }))}
                  placeholder={form.method === 'bank' ? 'e.g. BDO-88213410' : form.method === 'cash' ? 'e.g. OR-1042 (optional)' : 'e.g. GC-99214870'} />
              </div>
              <div>
                <label htmlFor="gh-pay-amount">Amount paid</label>
                <input id="gh-pay-amount" type="number" required min="1" value={form.amount} onChange={e => setForm(f => ({ ...f, amount: e.target.value }))} />
              </div>
              <div>
                <label htmlFor="gh-pay-date">Date of payment</label>
                <input id="gh-pay-date" type="date" required value={form.date} onChange={e => setForm(f => ({ ...f, date: e.target.value }))} />
              </div>
              <div>
                <label htmlFor="gh-pay-mobile">
                  {form.method === 'bank' ? 'Sending bank (optional)' : form.method === 'cash' ? 'Received by (optional)' : 'Your GCash mobile no. (optional)'}
                </label>
                <input
                  id="gh-pay-mobile" type="text" value={form.mobileNumber} onChange={e => setForm(f => ({ ...f, mobileNumber: e.target.value }))}
                  placeholder={form.method === 'bank' ? 'e.g. BDO, BPI' : form.method === 'cash' ? 'e.g. staff name' : '09XX XXX XXXX'} />
              </div>
              <div className="full">
                <button type="submit" className="gh-portal-btn-gold" disabled={busy}>{busy ? 'Submitting…' : 'Submit for confirmation'}</button>
              </div>
            </form>
            {toast && <div className="gh-portal-toast">{toast}</div>}
          </div>
        )}
      </div>

      <div className="gh-portal-section">
        <h4>Payment history</h4>
        {history.length === 0 ? (
          <div className="gh-portal-empty">No payments recorded yet.</div>
        ) : (
          <table className="gh-portal-table">
            <thead><tr><th>Date</th><th>Method</th><th>Reference</th><th>Amount</th><th>Status</th></tr></thead>
            <tbody>
              {history.map((p, i) => (
                <tr key={i}>
                  <td>{p.date}</td>
                  <td>{p.method || 'GCash'}</td>
                  <td>{p.referenceNumber || p.ref || '—'}</td>
                  <td>{fmt(p.amount)}</td>
                  <td>
                    {p._kind === 'confirmed' || p.status === 'confirmed'
                      ? <span className="gh-pill confirmed">Confirmed</span>
                      : <span className={`gh-pill ${p.status === 'rejected' ? 'rejected' : 'pending'}`}>
                          {p.status === 'rejected' ? 'Needs attention' : 'Awaiting confirmation'}
                        </span>}
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </div>
    </div>
  );
}


function CustomerPortal({ open, onClose }) {
  const [authUser, setAuthUser] = useState(undefined); // undefined = checking, null = logged out
  const [customer, setCustomer] = useState(undefined); // undefined = loading, null = no match found
  const [mode, setMode] = useState('login'); // 'login' | 'forgot'
  const [forgotEmail, setForgotEmail] = useState('');
  const cardRef = useRef(null);
  const previousFocusRef = useRef(null);

  useEffect(() => {
    if (!open || !window.auth) return;
    return window.auth.onAuthStateChanged(u => setAuthUser(u || null));
  }, [open]);

  useEffect(() => {
    if (!authUser || !window.db) { setCustomer(undefined); return; }
    return window.db.collection('customers').where('authUid', '==', authUser.uid).limit(1)
      .onSnapshot(
        snap => setCustomer(snap.empty ? null : { _id: snap.docs[0].id, ...snap.docs[0].data() }),
        err => { console.error('[GH portal] customer lookup failed:', err); setCustomer(null); }
      );
  }, [authUser]);

  // Accessibility: move focus into the dialog on open, restore it to whatever
  // triggered the dialog (the "My Account" badge, header button, etc.) on
  // close, and let Escape close it the same way clicking outside does.
  useEffect(() => {
    if (!open) return;
    previousFocusRef.current = document.activeElement;
    const focusTimer = setTimeout(() => { if (cardRef.current) cardRef.current.focus(); }, 0);
    const onKeyDown = (e) => { if (e.key === 'Escape') onClose(); };
    document.addEventListener('keydown', onKeyDown);
    return () => {
      clearTimeout(focusTimer);
      document.removeEventListener('keydown', onKeyDown);
      if (previousFocusRef.current && typeof previousFocusRef.current.focus === 'function') {
        previousFocusRef.current.focus();
      }
    };
  }, [open]);

  if (!open) return null;

  const logout = () => window.auth.signOut();

  return (
    <div className="gh-portal-overlay" onClick={e => e.target === e.currentTarget && onClose()}>
      <style>{`
        .gh-portal-overlay{ position:fixed; inset:0; background:rgba(20,20,18,.55); z-index:200; display:flex; align-items:center; justify-content:center; padding:20px; }
        .gh-portal-card{ background:var(--card); border-radius:16px; max-width:540px; width:100%; max-height:88vh;
          overflow-y:auto; position:relative; box-shadow:0 20px 50px rgba(0,0,0,.28); border:1px solid var(--line); }
        .gh-portal-close{ position:absolute; top:16px; right:18px; background:none; border:none; font-size:18px; cursor:pointer; color:var(--ink-2); line-height:1; z-index:2; }
        .gh-portal-header-block{ padding:32px 32px 30px; background:var(--card); }
        .gh-portal-brand{ display:flex; align-items:center; gap:10px; margin-bottom:22px; }
        .gh-portal-brand img{ width:32px; height:32px; object-fit:contain; flex-shrink:0; }
        .gh-portal-brand span{ font-size:12.5px; color:var(--ink-2); letter-spacing:.02em; }
        .gh-portal-title{ font-size:24px; font-weight:700; color:var(--ink); margin:0 0 6px; }
        .gh-portal-subtitle{ font-size:14px; color:var(--ink-2); margin:0 0 24px; }
        .gh-portal-form label, .gh-portal-form-grid label{ display:block; font-size:13px; font-weight:500; color:var(--ink); margin-bottom:6px; margin-top:16px; }
        .gh-portal-form label:first-child{ margin-top:0; }
        .gh-portal-form input, .gh-portal-form-grid input{ width:100%; padding:10px 14px; border:1px solid var(--line); border-radius:10px; font-size:14.5px; font-family:inherit; background:var(--bg); color:var(--ink); box-shadow:0 1px 2px rgba(0,0,0,.04); }
        .gh-portal-form input:focus, .gh-portal-form-grid input:focus{ outline:none; border-color:var(--accent); box-shadow:0 0 0 3px rgba(47,93,76,.12); }
        .gh-portal-btn-primary{ width:100%; margin-top:24px; background:var(--accent); color:var(--accent-ink); border:none; padding:13px; border-radius:10px; font-weight:600; font-size:14.5px; cursor:pointer; box-shadow:0 6px 14px rgba(47,93,76,.2); }
        .gh-portal-btn-primary:hover{ opacity:.94; }
        .gh-portal-btn-gold{ background:var(--gold); color:#fff; border:none; padding:10px 20px; border-radius:8px; font-weight:700; font-size:13.5px; cursor:pointer; }
        .gh-portal-link-btn{ background:none; border:none; color:var(--ink-2); font-size:13px; cursor:pointer; text-decoration:underline; margin-top:14px; padding:0; display:block; text-align:center; width:100%; }
        .gh-portal-link-btn:hover{ color:var(--accent); }
        .gh-portal-err{ color:var(--terracotta); font-size:13px; margin-top:12px; }
        .gh-portal-footer-strip{ background:var(--bg); border-top:1px solid var(--line); padding:16px 32px; text-align:center; font-size:13px; color:var(--ink-2); }
        .gh-portal-footer-strip button{ background:none; border:none; font-family:inherit; font-size:13px; font-weight:600; color:var(--ink); cursor:pointer; margin-left:4px; padding:0; text-decoration:underline; }
        .gh-portal-topbar{ display:flex; justify-content:space-between; align-items:center; margin-bottom:20px; font-size:14px; color:var(--ink-2); }
        .gh-portal-hero{ background:var(--accent); color:var(--accent-ink); border-radius:10px; padding:22px 24px; margin-bottom:16px; }
        .gh-portal-hero-label{ font-size:12.5px; opacity:.8; margin-bottom:4px; }
        .gh-portal-hero-amount{ font-size:32px; font-weight:700; }
        .gh-portal-hero-meta{ font-size:12.5px; opacity:.85; margin-top:6px; }
        .gh-portal-progress{ margin-top:16px; }
        .gh-portal-progress-track{ background:rgba(255,255,255,.22); border-radius:999px; height:8px; overflow:hidden; }
        .gh-portal-progress-fill{ background:var(--gold); height:100%; border-radius:999px; transition:width .4s ease; }
        .gh-portal-progress-caption{ display:flex; justify-content:space-between; gap:10px; font-size:11.5px; opacity:.85; margin-top:7px; }
        .gh-portal-stats{ display:flex; flex-wrap:wrap; gap:1px; background:var(--line); border:1px solid var(--line); border-radius:10px; overflow:hidden; margin-bottom:28px; }
        .gh-portal-stats > div{ flex:1; min-width:110px; background:var(--card); padding:12px 14px; }
        .gh-portal-stats .k{ font-size:11.5px; color:var(--ink-2); margin-bottom:4px; }
        .gh-portal-stats .v{ font-size:15px; font-weight:700; color:var(--ink); }
        .gh-portal-section{ margin-bottom:28px; }
        .gh-portal-section h4{ font-size:15px; margin:0 0 10px; color:var(--ink); border-bottom:1px solid var(--line); padding-bottom:8px; }
        .gh-portal-table{ width:100%; border-collapse:collapse; font-size:13px; }
        .gh-portal-table th{ text-align:left; font-size:11px; color:var(--ink-2); padding:6px 8px; border-bottom:1px solid var(--line); }
        .gh-portal-table td{ padding:9px 8px; border-bottom:1px solid var(--line); }
        .gh-pill{ font-size:11px; padding:3px 9px; border-radius:20px; }
        .gh-pill.confirmed{ background:#e4ede6; color:#2f5d4c; }
        .gh-pill.pending{ background:#f3e9d8; color:#8a6a2e; }
        .gh-pill.rejected{ background:#f5e3df; color:#a85c4b; }
        .gh-portal-help{ font-size:13px; color:var(--ink-2); margin:0 0 14px; }
        .gh-portal-form-grid{ display:grid; grid-template-columns:1fr 1fr; gap:0 16px; }
        .gh-portal-form-grid .full{ grid-column:1/-1; margin-top:16px; }
        .gh-portal-toast{ margin-top:14px; font-size:13px; color:var(--accent); background:var(--card); border:1px solid var(--line); padding:10px 12px; border-radius:8px; }
        .gh-portal-empty{ font-size:13.5px; color:var(--ink-2); padding:14px 0; }
        .gh-portal-dashboard-pad{ padding:32px; }
        @media (max-width:520px){ .gh-portal-form-grid{ grid-template-columns:1fr; } }
      `}</style>
      <div className="gh-portal-card" ref={cardRef} tabIndex={-1} role="dialog" aria-modal="true" aria-label="Golden Harmonic Memorial Park customer portal" style={{ outline: 'none' }}>
        <button className="gh-portal-close" onClick={onClose} aria-label="Close">✕</button>

        {authUser === undefined ? (
          <div style={{ padding: '60px 32px', textAlign: 'center', color: 'var(--ink-2)' }}>Checking login…</div>
        ) : !authUser ? (
          <React.Fragment>
            <div className="gh-portal-header-block">
              <div className="gh-portal-brand">
                <img src="/logo-header.png" alt="Golden Harmonic Memorial Park logo" />
                <span>Golden Harmonic — Customer Portal</span>
              </div>
              {mode === 'login' ? (
                <React.Fragment>
                  <h2 className="gh-portal-title">Welcome back</h2>
                  <p className="gh-portal-subtitle">Sign in to view your balance and payment history.</p>
                  <PortalLogin onForgot={(email) => { setForgotEmail(email); setMode('forgot'); }} />
                </React.Fragment>
              ) : (
                <React.Fragment>
                  <h2 className="gh-portal-title">Reset your password</h2>
                  <p className="gh-portal-subtitle">We'll email you a link to set a new one.</p>
                  <PortalForgot initialEmail={forgotEmail} onBack={() => setMode('login')} />
                </React.Fragment>
              )}
            </div>
            <div className="gh-portal-footer-strip">
              Don't have portal access yet?
              <button onClick={() => { /* informational only */ }} style={{ cursor: 'default', textDecoration: 'none' }}>Ask our staff to set it up</button>
            </div>
          </React.Fragment>
        ) : customer === undefined ? (
          <div style={{ padding: '60px 32px', textAlign: 'center', color: 'var(--ink-2)' }}>Loading your account…</div>
        ) : customer === null ? (
          <div className="gh-portal-dashboard-pad">
            <p style={{ fontSize: 14, color: 'var(--ink-2)' }}>
              We couldn't find a customer record linked to this login. Please contact our staff so they can connect your account.
            </p>
            <button className="gh-portal-link-btn" onClick={logout}>Log out</button>
          </div>
        ) : (
          <div className="gh-portal-dashboard-pad">
            <PortalDashboard customer={customer} onLogout={logout} />
          </div>
        )}
      </div>
    </div>
  );
}

/* ---------- Cart Drawer & Checkout ----------
   Mirrors the submission pattern used by Brochure() (inquiries + customers
   in Firestore, best-effort EmailJS confirmation) but for a multi-item
   cart. Payment stays manual: after submitting, staff follow up with
   GCash details the same way they do for every other lead today. */
function CartDrawer({ open, onClose }) {
  const { cart, removeFromCart, updateQty, clearCart } = useCart();
  const { openPresale } = React.useContext(window.PresaleContext);
  const [step, setStep] = useState('cart'); // 'cart' | 'checkout' | 'sent'
  const [form, setForm] = useState({ firstName: '', lastName: '', email: '', phone: '', paymentMethod: 'gcash', term: 'cash' });
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState('');
  const cardRef = useRef(null);
  const previousFocusRef = useRef(null);

  useBodyScrollLock(open);

  useEffect(() => {
    if (!open) { setStep('cart'); setError(''); return; }
    previousFocusRef.current = document.activeElement;
    const focusTimer = setTimeout(() => { if (cardRef.current) cardRef.current.focus(); }, 0);
    const onKeyDown = (e) => { if (e.key === 'Escape') onClose(); };
    document.addEventListener('keydown', onKeyDown);
    return () => {
      clearTimeout(focusTimer);
      document.removeEventListener('keydown', onKeyDown);
      if (previousFocusRef.current && typeof previousFocusRef.current.focus === 'function') {
        previousFocusRef.current.focus();
      }
    };
  }, [open]);

  if (!open) return null;

  const selectedTerm = PLAN_TERMS.find((t) => t.key === form.term) || PLAN_TERMS[0];
  // Burial plots (Regular, Premium, Corner Premium) are reserved on the presale page, not through this cart order.
  const plotItems = cart.filter((i) => (window.PRESALE_TYPE_IDS || []).includes(i.id));
  const otherItems = cart.filter((i) => !(window.PRESALE_TYPE_IDS || []).includes(i.id));
  const reservePlots = () => {
    const first = plotItems[0];
    plotItems.forEach((p) => removeFromCart(p.id));
    onClose();
    openPresale(first.id);
  };
  const rawSubtotal = cart.reduce((sum, i) => sum + i.price * i.qty, 0);
  const subtotal = rawSubtotal * (1 + selectedTerm.surcharge);
  const monthly = selectedTerm.months ? subtotal / selectedTerm.months : null;
  const itemCount = cart.reduce((sum, i) => sum + i.qty, 0);
  const set = (k) => (e) => setForm((f) => ({ ...f, [k]: e.target.value }));

  const submit = async (e) => {
    e.preventDefault();
    setSaving(true);
    setError('');
    try {
      if (window.db) {
        const ts = firebase.firestore.FieldValue.serverTimestamp();
        const base = {
          firstName: form.firstName,
          lastName: form.lastName,
          fullName: `${form.firstName} ${form.lastName}`,
          email: form.email,
          phone: form.phone,
          source: 'website',
          createdAt: ts,
          items: cart.map((i) => ({
            planId: i.id, planName: i.name, planPrice: i.price, qty: i.qty, lineTotal: i.price * i.qty
          })),
          totalPrice: subtotal,
          paymentTerm: selectedTerm.label,
          termMonths: selectedTerm.months,
          termSurcharge: selectedTerm.surcharge,
          paymentMethod: form.paymentMethod === 'bank' ? 'Bank Transfer' : 'GCash'
        };
        // 1. Save the order
        const orderRef = await window.db.collection('orders').add({
          ...base,
          type: 'cart_checkout',
          status: 'pending_payment'
        });
        // 2. Save customer / lead, linked to the order
        await window.db.collection('customers').add({
          ...base,
          status: 'lead',
          orderIds: [orderRef.id]
        });
      }

      // Best-effort confirmation email — quietly no-ops if EmailJS isn't configured.
      if (window.emailjs && window.EMAILJS_CONFIG && window.EMAILJS_CONFIG.PUBLIC_KEY !== 'YOUR_PUBLIC_KEY') {
        emailjs.send(window.EMAILJS_CONFIG.SERVICE_ID, window.EMAILJS_CONFIG.TEMPLATE_ID, {
          to_email: form.email,
          first_name: form.firstName,
          last_name: form.lastName,
          phone: form.phone,
          plan_name: cart.map((i) => `${i.name}${i.qty > 1 ? ` ×${i.qty}` : ''}`).join(', '),
          plan_price: fmt(subtotal),
          payment_term: selectedTerm.label,
          payment_method: form.paymentMethod === 'bank' ? 'Bank Transfer' : 'GCash'
        }).catch((err) => console.error('[GH] Email send error:', err));
      }

      clearCart();
      setStep('sent');
    } catch (err) {
      console.error('[GH] Order save error:', err);
      setError('Could not submit your order. Please call us at +63 917 123 4567.');
    } finally {
      setSaving(false);
    }
  };

  return (
    <div className="gh-cart-overlay" onClick={(e) => e.target === e.currentTarget && onClose()}>
      <style>{`
        .gh-cart-overlay{ position:fixed; inset:0; background:rgba(20,20,18,.5); z-index:200; display:flex; justify-content:flex-end; }
        .gh-cart-panel{ background:var(--card); width:min(420px,100%); max-width:100%; height:100%;
          overflow-y:auto; position:relative; box-shadow:-12px 0 40px rgba(0,0,0,.22); border-left:1px solid var(--line);
          padding-top:env(safe-area-inset-top,0px); padding-bottom:env(safe-area-inset-bottom,0px); display:flex; flex-direction:column; }
        .gh-cart-close{ position:absolute; top:16px; right:18px; background:none; border:none; font-size:18px; cursor:pointer; color:var(--ink-2); line-height:1; z-index:2; }
        .gh-cart-head{ padding:28px 28px 18px; border-bottom:1px solid var(--line); flex-shrink:0; }
        .gh-cart-head h2{ font-size:20px; font-weight:700; color:var(--ink); margin:0; }
        .gh-cart-items{ padding:8px 28px; flex:1; overflow-y:auto; }
        .gh-cart-empty{ font-size:13.5px; color:var(--ink-2); padding:40px 0; text-align:center; line-height:1.6; }
        .gh-cart-item{ display:flex; justify-content:space-between; align-items:flex-start; gap:12px; padding:16px 0; border-bottom:1px solid var(--line); }
        .gh-cart-item .name{ font-size:14.5px; font-weight:600; color:var(--ink); }
        .gh-cart-item .cat{ font-size:12px; color:var(--ink-2); margin-top:2px; }
        .gh-cart-item .price{ font-size:14px; color:var(--ink); white-space:nowrap; }
        .gh-cart-remove{ font-size:12px; color:var(--ink-2); background:none; border:none; cursor:pointer; text-decoration:underline; padding:0; margin-top:8px; }
        .gh-cart-remove:hover{ color:var(--terracotta); }
        .gh-cart-foot{ padding:20px 28px 28px; border-top:1px solid var(--line); flex-shrink:0; }
        .gh-cart-subtotal{ display:flex; justify-content:space-between; align-items:baseline; margin-bottom:16px; }
        .gh-cart-subtotal .lbl{ font-size:13px; color:var(--ink-2); }
        .gh-cart-subtotal .val{ font-size:19px; font-weight:700; color:var(--accent); }
        .gh-cart-btn{ width:100%; background:var(--accent); color:var(--accent-ink); border:none; padding:13px; border-radius:10px; font-weight:600; font-size:14.5px; cursor:pointer; box-shadow:0 6px 14px rgba(47,93,76,.2); }
        .gh-cart-btn:hover{ opacity:.94; }
        .gh-cart-btn:disabled{ opacity:.5; cursor:not-allowed; }
        .gh-cart-note{ font-size:12px; color:var(--ink-2); margin-top:10px; text-align:center; line-height:1.5; }
        .gh-cart-form{ padding:18px 28px 28px; overflow-y:auto; }
        .gh-cart-form label{ display:block; font-size:13px; font-weight:500; color:var(--ink); margin-bottom:6px; margin-top:16px; }
        .gh-cart-form label:first-child{ margin-top:0; }
        .gh-cart-form input{ width:100%; padding:10px 14px; border:1px solid var(--line); border-radius:10px; font-size:14.5px; font-family:inherit; background:var(--bg); color:var(--ink); box-shadow:0 1px 2px rgba(0,0,0,.04); }
        .gh-cart-form input:focus{ outline:none; border-color:var(--accent); box-shadow:0 0 0 3px rgba(47,93,76,.12); }
        .gh-cart-back{ background:none; border:none; font-size:13px; color:var(--ink-2); cursor:pointer; padding:0; margin-bottom:14px; text-decoration:underline; }
        .gh-cart-error{ font-size:13px; color:#a85c4b; margin-top:12px; }
      `}</style>
      <div className="gh-cart-panel" ref={cardRef} tabIndex={-1} role="dialog" aria-modal="true" aria-label="Shopping cart" style={{ outline: 'none' }}>
        <button className="gh-cart-close" onClick={onClose} aria-label="Close cart">✕</button>

        {step === 'sent' ? (
          <div style={{ padding: '60px 28px', textAlign: 'center' }}>
            <div style={{
              width: 56, height: 56, borderRadius: '50%', background: 'var(--accent)',
              color: 'var(--accent-ink)', margin: '0 auto 20px',
              display: 'grid', placeItems: 'center', fontSize: 26
            }}>✓</div>
            <h3 style={{ marginBottom: 10, color: 'var(--ink)' }}>Order request sent.</h3>
            <p style={{ color: 'var(--ink-2)', fontSize: 13.5, lineHeight: 1.6 }}>
              Check your inbox for a confirmation. Our staff will reach out with GCash payment details to complete your reservation.
            </p>
            <button className="gh-cart-btn" style={{ marginTop: 24 }} onClick={onClose}>Close</button>
          </div>
        ) : (
          <React.Fragment>
            <div className="gh-cart-head">
              <h2>{step === 'checkout' ? 'Your details' : 'Your Cart'}</h2>
            </div>

            {step === 'cart' ? (
              <React.Fragment>
                <div className="gh-cart-items">
                  {cart.length === 0 ? (
                    <p className="gh-cart-empty">Your cart is empty.<br />Browse our plots and add one to get started.</p>
                  ) : cart.map((item) => (
                    <div className="gh-cart-item" key={item.id} style={{ display: 'flex', gap: 12, alignItems: 'flex-start' }}>
                      {item.image ?
                      <img src={item.image} alt={item.name} style={{
                          width: 56, height: 56, borderRadius: 8, objectFit: 'cover', flexShrink: 0, border: '1px solid var(--line)'
                        }} /> :

                      <div style={{
                          width: 56, height: 56, borderRadius: 8, flexShrink: 0, background: 'var(--bg)',
                          border: '1px solid var(--line)', display: 'flex', alignItems: 'center', justifyContent: 'center'
                        }}>
                          <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--ink-2)" strokeWidth="1.5"><rect x="3" y="3" width="18" height="18" rx="2" /><circle cx="8.5" cy="8.5" r="1.5" /><path d="M21 15l-5-5L5 21" /></svg>
                        </div>}

                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div className="name">{item.name}</div>
                        <div className="cat">{item.category}</div>
                        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8, marginTop: 8 }}>
                          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                            <div style={{
                              display: 'flex', alignItems: 'center', border: '1px solid var(--line)',
                              borderRadius: 7, overflow: 'hidden'
                            }}>
                              <button
                                type="button"
                                aria-label={`Decrease quantity of ${item.name}`}
                                onClick={() => updateQty(item.id, item.qty - 1)}
                                style={{ width: 26, height: 26, border: 'none', background: 'var(--bg)', color: 'var(--ink)', fontSize: 14, cursor: 'pointer', lineHeight: 1 }}>−</button>
                              <span style={{ width: 26, textAlign: 'center', fontSize: 12.5, fontWeight: 600, color: 'var(--ink)' }}>{item.qty}</span>
                              <button
                                type="button"
                                aria-label={`Increase quantity of ${item.name}`}
                                onClick={() => updateQty(item.id, item.qty + 1)}
                                style={{ width: 26, height: 26, border: 'none', background: 'var(--bg)', color: 'var(--ink)', fontSize: 14, cursor: 'pointer', lineHeight: 1 }}>+</button>
                            </div>
                            <button className="gh-cart-remove" style={{ marginTop: 0 }} onClick={() => removeFromCart(item.id)}>Remove</button>
                          </div>
                          <div className="price" style={{ textAlign: 'right' }}>
                            {fmt(item.price * item.qty)}
                            {item.qty > 1 && <div style={{ fontSize: 11, color: 'var(--ink-2)', marginTop: 2 }}>{fmt(item.price)} each</div>}
                          </div>
                        </div>
                      </div>
                    </div>
                  ))}
                </div>
                <div className="gh-cart-foot">
                  <div className="gh-cart-subtotal">
                    <span className="lbl">Subtotal</span>
                    <span className="val">{fmt(rawSubtotal)}</span>
                  </div>
                  {plotItems.length > 0 ? (
                    <React.Fragment>
                      <div style={{ background: '#fff3c9', border: '1.5px solid #d9a62e', borderRadius: 10, padding: '10px 12px', fontSize: 13.5, lineHeight: 1.5, color: '#3b2a05', marginBottom: 10 }}>
                        Plots are reserved first come, first served. Press the button below to choose your exact plot on the map and fill in your details.
                      </div>
                      <button className="gh-cart-btn" onClick={reservePlots}>
                        Choose my plot and reserve →
                      </button>
                      {otherItems.length > 0 && (
                        <button className="gh-cart-btn" style={{ marginTop: 8, background: 'transparent', color: 'var(--ink)', border: '1px solid var(--line)' }} onClick={() => { plotItems.forEach((p) => removeFromCart(p.id)); setStep('checkout'); }}>
                          Checkout the other items only
                        </button>
                      )}
                      <p className="gh-cart-note">Opens in a new tab. No payment is taken yet.</p>
                    </React.Fragment>
                  ) : (
                    <React.Fragment>
                      <button className="gh-cart-btn" disabled={cart.length === 0} onClick={() => setStep('checkout')}>
                        Proceed to Checkout
                      </button>
                      <p className="gh-cart-note">No payment is taken yet — you'll confirm your details next.</p>
                    </React.Fragment>
                  )}
                </div>
              </React.Fragment>
            ) : (
              <form className="gh-cart-form" onSubmit={submit}>
                <button type="button" className="gh-cart-back" onClick={() => setStep('cart')}>← Back to cart</button>
                <label>First name<input required value={form.firstName} onChange={set('firstName')} /></label>
                <label>Last name<input required value={form.lastName} onChange={set('lastName')} /></label>
                <label>Email<input type="email" required value={form.email} onChange={set('email')} /></label>
                <label>Phone<input required value={form.phone} onChange={set('phone')} /></label>
                <label>
                  How will you pay?
                  <div style={{ display: 'flex', gap: 8, marginTop: 2 }}>
                    {[
                    { key: 'gcash', label: 'GCash' },
                    { key: 'bank', label: 'Bank Transfer' }].
                    map((opt) => (
                      <button
                        key={opt.key}
                        type="button"
                        onClick={() => setForm((f) => ({ ...f, paymentMethod: opt.key }))}
                        style={{
                          flex: 1, padding: '10px 12px', borderRadius: 10, fontSize: 13.5, fontWeight: 600,
                          cursor: 'pointer', fontFamily: 'inherit',
                          border: form.paymentMethod === opt.key ? '1px solid var(--accent)' : '1px solid var(--line)',
                          background: form.paymentMethod === opt.key ? 'var(--accent)' : 'var(--bg)',
                          color: form.paymentMethod === opt.key ? 'var(--accent-ink)' : 'var(--ink)'
                        }}>
                        {opt.label}
                      </button>
                    ))}
                  </div>
                </label>
                <label>
                  Payment term
                  <select
                    value={form.term}
                    onChange={set('term')}
                    style={{
                      width: '100%', padding: '10px 14px', borderRadius: 10, fontSize: 14.5,
                      border: '1px solid var(--line)', background: 'var(--bg)', color: 'var(--ink)',
                      fontFamily: 'inherit', boxShadow: '0 1px 2px rgba(0,0,0,.04)'
                    }}>
                    {PLAN_TERMS.map((t) => (
                      <option key={t.key} value={t.key}>
                        {t.label}{t.surcharge > 0 ? ` (+${Math.round(t.surcharge * 100)}% surcharge)` : ' (no surcharge)'}
                      </option>
                    ))}
                  </select>
                </label>
                <div className="gh-cart-subtotal" style={{ marginTop: 20 }}>
                  <span className="lbl">Total ({itemCount} {itemCount === 1 ? 'plot' : 'plots'})</span>
                  <span className="val">{fmt(subtotal)}</span>
                </div>
                {monthly &&
                <p style={{ fontSize: 12.5, color: 'var(--ink-2)', marginTop: -10, marginBottom: 4, textAlign: 'right' }}>
                    {fmt(monthly)}/mo · {selectedTerm.months}mo
                  </p>}

                <button className="gh-cart-btn" type="submit" disabled={saving}>
                  {saving ? 'Submitting…' : 'Submit Order'}
                </button>
                {error && <p className="gh-cart-error">{error}</p>}
                <p className="gh-cart-note">
                  {form.paymentMethod === 'bank' ?
                  "Our staff will follow up with our bank account details to complete your reservation." :
                  "Our staff will follow up with GCash payment instructions — same as our current process."}
                </p>
              </form>
            )}
          </React.Fragment>
        )}
      </div>
    </div>);
}

function Footer() {
  return (
    <footer>
      <div className="wrap">
        <div className="grid">
          <div>
            <div className="brand" style={{ marginBottom: 18 }}>
              <img src="/logo-header.png" alt="Golden Harmonic Memorial Park logo"
                style={{ width: 44, height: 44, objectFit: 'contain' }} />
              <div className="brand-text">
                <div className="name">Golden Harmonic</div>
                <div className="sub">Memorial Park · Palawan</div>
              </div>
            </div>
            <p style={{ fontSize: 14, color: '#b6ad97', maxWidth: 320 }}>
              A memorial park in Palawan — caring for families for generations.
            </p>
          </div>
          <div>
            <h4>Plots</h4>
            <ul>
              <li><a href="#tiers">Garden Lawn Lots</a></li>
              <li><a href="#tiers">Family Estates</a></li>
              <li><a href="#tiers">Mausoleum Suites</a></li>
              <li><a href="#tiers">Columbarium Niches</a></li>
            </ul>
          </div>
          <div>
            <h4>Services</h4>
            <ul>
              <li><a href="#plans">Payment Plans</a></li>
              <li><a href="#">Funeral services</a></li>
              <li><a href="#">Venue rental</a></li>
            </ul>
          </div>
          <div>
            <h4>Visit</h4>
            <ul>
              <li><a href="#brochure">Palawan · 4.9 ha</a></li>
              <li><a href="tel:+639171234567">+63 917 123 4567</a></li>
              <li><a href="mailto:hello@goldenharmonic.ph">hello@goldenharmonic.ph</a></li>
            </ul>
          </div>
        </div>
        <div className="legal">
          <div>© 2026 Golden Harmonic Memorial Park, Inc. · DHSUD LTS-2018-0421 · SEC CS201801234</div>
          <div><a href="/privacy.html">Privacy Policy</a> · <a href="/terms.html">Terms of Service</a> · Perpetual Care Trust Report</div>
        </div>
      </div>
    </footer>);

}

/* ---------- Tweaks panel ---------- */
function Tweaks({ t, setTweak, priceMult }) {
  return (
    <TweaksPanel title="Tweaks">
      <TweakSection label="Color palette">
        <TweakColor
          label="Theme"
          value={t.palette}
          onChange={(v) => setTweak('palette', v)}
          options={PALETTE_OPTIONS} />

      </TweakSection>
      <TweakSection label="Pricing">
        <TweakSlider
          label="Price level"
          value={t.priceMultiplier}
          onChange={(v) => setTweak('priceMultiplier', v)}
          min={50} max={200} step={5}
          unit="%" />

        <div style={{ fontSize: 11, color: 'rgba(41,38,27,.55)', marginTop: 4, padding: '0 4px', lineHeight: 1.6, fontFamily: 'JetBrains Mono, monospace' }}>
          Regular Plot · {fmt(30000 * priceMult)}<br />
          Family Vault · {fmt(105000 * priceMult)}<br />
          Corner Prime Garden · {fmt(57500 * priceMult)}
        </div>
      </TweakSection>
    </TweaksPanel>);

}

/* ---------- Mount ---------- */
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
