/* eslint-disable */
/* cityform v2 — Cinematic home page (revision 2)

   Changes from rev 1:
   · Sticker DEMOTED everywhere it was a marketing motif. The customer
     buys the OBJECT — the brand's hero is the model itself, not a label
     on its lid. Stickers still appear on the product spec / packaging
     tabs and inside the commission flow (real product detail), never on
     marketing-only sections.
   · Hero is now tweak-selectable: rotating 3D model (default) / photo
     gallery / plan view / triptych — exposed in the Tweaks panel.
   · "City Marks Wall" is now "The Twelve" — photographed objects, not
     stickers.
   · New "See it on your shelf" AR module on the home page, surfacing
     the existing AR Quick Look feature from the commission confirm step.
   · Process scene phase 04 uses the real Sheffield on-base render.
   · All sections respect the global motion-density and hide-stickers
     tweaks via window.__v2 read by useV2.
*/

function useV2() {
  // The v2 tweaks (app-v2.jsx) writes its current state to window.__v2 so
  // page-level components can read it without explicit threading.
  const [v, setV] = React.useState(() => window.__v2 || {});
  React.useEffect(() => {
    const handler = () => setV({ ...(window.__v2 || {}) });
    window.addEventListener('cf-v2-tweaks', handler);
    return () => window.removeEventListener('cf-v2-tweaks', handler);
  }, []);
  return v;
}

function HomePage({ go, t }) {
  const v2 = useV2();
  const featured = window.CATALOG.filter(c => c.featured && c.accent).slice(0, 6);
  const newIn = window.CATALOG.filter(c => !c.featured).slice(0, 4);
  // Sheffield is the only city with a bundled, viewer-tuned GLB
  // (assets/products/sheffield.glb). CATALOG[0] is the live admin order
  // (not necessarily Sheffield), so pick Sheffield explicitly and fall
  // back to the first entry only if it's somehow absent.
  const heroCity = window.CATALOG.find(c => c.id === 'sheffield') || window.CATALOG[0];

  return (
    <div>
      <HeroV2 city={heroCity} go={go} mode={v2.heroMode || 'model'}/>
      <MarqueeStripV2/>
      <SignatureProcess mode={v2.sceneMode || 'scroll'}/>
      <FeaturedCollection items={featured} go={go}/>
      <TheTwelveGrid go={go}/>
      <CommissionPreviewV2 go={go}/>
      <ARShelfModule/>
      <PullQuoteV2/>
      <ArchiveSection items={newIn} go={go}/>
      <PressStripV2/>
      <Footer go={go}/>
    </div>
  );
}

/* ────────────────────────────── 01 HERO */
function HeroV2({ city, go, mode = 'model' }) {
  const [imgRef, imgY] = useParallax(34);

  return (
    <section style={{ background:'var(--mist)', position:'relative', overflow:'hidden', borderBottom:'1px solid var(--line)' }}>
      <OsContourBg seed="sheffield-hero" density={11} opacity={0.12}/>
      <div className="coord-grid" style={{ opacity:0.5 }}/>

      {/* Top stamp */}
      <div style={{ position:'absolute', top:36, left:36, zIndex:5, display:'flex', alignItems:'center', gap:14 }}>
        <span className="eyebrow" style={{ fontSize:10, color:'var(--ink)' }}>SHEFFIELD · 53.3811° N · 1.4701° W</span>
        <span className="v-rule" style={{ color:'var(--ink)' }}/>
        <span className="eyebrow" style={{ fontSize:10 }}>VOLUME 02 · MAY 2026</span>
      </div>
      <div style={{ position:'absolute', top:32, right:36, zIndex:5 }}>
        <NorthMark size={28}/>
      </div>

      <div className="container" style={{
        position:'relative', zIndex:2,
        paddingTop:96, paddingBottom:96,
        display:'grid', gridTemplateColumns:'1.05fr 1fr', gap:64, alignItems:'center',
      }}>
        {/* LEFT — copy */}
        <div style={{ position:'relative' }}>
          <Reveal kind="reveal" delay={120}>
            <div style={{ display:'flex', alignItems:'center', gap:14, marginBottom:32 }}>
              <span style={{ width:48, height:1, background:'var(--ink)' }}/>
              <span className="eyebrow" style={{ fontSize:10 }}>MADE TO ORDER · A LETTER IN FORM</span>
            </div>
          </Reveal>
          <h1 className="display-hero" style={{ fontSize:'clamp(76px, 9vw, 148px)', margin:0 }}>
            <RisingText lines={['A square kilometre.', 'Of where you live.', <em key="3" className="serif-accent">Scaled to nine.</em>]} delay={200} lineDelay={140}/>
          </h1>
          <Reveal kind="reveal" delay={780}>
            <p className="body" style={{ fontSize:19.5, lineHeight:1.55, color:'var(--ink-soft)', maxWidth:540, marginTop:36 }}>
              Cityform makes architecturally considered miniature models of British cities. Each is cut from the same Environment Agency survey data planners use, scaled to nine centimetres on your shelf.
            </p>
          </Reveal>
          <Reveal kind="reveal" delay={920} style={{ display:'flex', gap:14, alignItems:'center', marginTop:40 }}>
            <button className="btn-v2" onClick={() => go('atelier')}>
              Commission a city <span className="btn-arrow">{Icon.arrow}</span>
            </button>
            <button className="btn-v2 btn-v2-ghost" onClick={() => go('atlas')}>
              Browse the twelve
            </button>
          </Reveal>
          <Reveal kind="reveal" delay={1100} style={{ marginTop:64, display:'flex', gap:48 }}>
            {[
              { v:'12', l:'In the collection' },
              { v:'9 × 9 cm', l:'One format' },
              { v:'1:11000', l:'Common scale' },
              { v:'7 days', l:'From order' },
            ].map((s, i) => (
              <div key={i}>
                <div className="tnum" style={{ fontSize:30, fontWeight:600, letterSpacing:'-0.018em' }}>{s.v}</div>
                <div className="eyebrow" style={{ marginTop:8, fontSize:9 }}>{s.l.toUpperCase()}</div>
              </div>
            ))}
          </Reveal>
        </div>

        {/* RIGHT — variant stage */}
        <div style={{ position:'relative', alignSelf:'stretch', minHeight:700 }} ref={imgRef}>
          <Reveal kind="reveal-clip" delay={300} style={{ position:'relative', height:'100%' }}>
            {mode === 'model'    && <HeroModel city={city}/>}
            {mode === 'photo'    && <HeroPhotoStack city={city}/>}
            {mode === 'plan'     && <HeroPlan city={city}/>}
            {mode === 'triptych' && <HeroTriptych city={city}/>}
          </Reveal>

          {/* Coord stamp — replaces the old floating sticker */}
          <Reveal kind="reveal-fade" delay={1000} style={{
            position:'absolute', bottom:-22, right:-24,
            background:'var(--bone)', border:'1px solid var(--ink)',
            padding:'14px 18px', minWidth:210, zIndex:4,
          }}>
            <div className="eyebrow" style={{ fontSize:9 }}>NOW SHIPPING</div>
            <div style={{ marginTop:6, fontSize:18, fontWeight:600, letterSpacing:'-0.01em' }}>{city.name}</div>
            <div className="tnum" style={{ marginTop:4, fontSize:11, color:'var(--stone)' }}>
              {city.coords.lat.toFixed(4)}° {city.coords.ns} · {Math.abs(city.coords.lng).toFixed(4)}° {city.coords.ew}
            </div>
            <div style={{ marginTop:10, paddingTop:10, borderTop:'1px dotted var(--line-strong)',
              display:'flex', alignItems:'center', justifyContent:'space-between' }}>
              <span style={{ display:'flex', alignItems:'center', gap:8 }}>
                <span style={{ width:8, height:8, background: city.accent ? `var(--${city.accent})` : 'var(--ink)' }}/>
                <span className="eyebrow" style={{ fontSize:9 }}>{(city.landmark || '—').toUpperCase()}</span>
              </span>
              <span className="tnum" style={{ fontSize:12, fontWeight:600 }}>£{city.price || 95}</span>
            </div>
          </Reveal>
        </div>
      </div>

      {/* Below-hero hero-mode strip — quietly hints the variants exist */}
      <div style={{ borderTop:'1px solid var(--line)', background:'var(--bone)' }}>
        <div className="container" style={{ display:'flex', alignItems:'center', justifyContent:'space-between', height:48 }}>
          <span className="eyebrow" style={{ fontSize:9 }}>
            VIEW · {mode === 'model' ? '3D MODEL' : mode === 'photo' ? 'PHOTO GALLERY' : mode === 'plan' ? 'PLAN' : 'TRIPTYCH'}
          </span>
          <span className="eyebrow" style={{ fontSize:9, color:'var(--stone)' }}>
            DRAG TO ROTATE · TWEAKS PANEL FOR ALTERNATIVES →
          </span>
        </div>
      </div>
    </section>
  );
}

/* Hero variant A — rotating 3D model (default) */
function HeroModel({ city }) {
  // Per-city: use this city's GLB when the catalogue has one; otherwise
  // Model3DViewer falls back to the photo (no Sheffield mesh under a
  // non-Sheffield hero). Bundled-asset path kept as a last resort only
  // when the hero IS Sheffield.
  const glb = city.model_glb || (city.id === 'sheffield' ? 'assets/products/sheffield.glb' : null);
  const fallback = city.on_base || (city.photos && city.photos[0]) || city.photo;
  const _sLat = city.coords.ns === 'S' ? -city.coords.lat : city.coords.lat;
  const _sLng = city.coords.ew === 'W' ? -city.coords.lng : city.coords.lng;
  const label3d = {
    title: (city.name || '').toUpperCase(),
    subline: '',
    coord: window.fmtCoord
      ? window.fmtCoord(_sLat, _sLng)
      : `${city.coords.lat.toFixed(3)}° ${city.coords.ns} · ${Math.abs(city.coords.lng).toFixed(3)}° ${city.coords.ew}`,
    showCoords: true,
  };
  return (
    <div style={{
      position:'relative', height:700, background:'var(--mist)',
      border:'1px solid var(--ink)', overflow:'hidden',
    }}>
      {/* Model3DViewer owns its own fallback: it shows the `fallback`
         photo only when the GLB genuinely fails (phase === 'error').
         No always-on backdrop here — that double-exposed a static photo
         behind the live rotating model. */}
      <Model3DViewer url={glb} fallback={fallback} label={label3d} height={700} autoRotateSpeed={0.6}/>
      {/* Frame plate */}
      <div style={{ position:'absolute', top:18, left:18,
        background:'var(--bone)', border:'1px solid var(--ink)', padding:'8px 12px',
        display:'flex', alignItems:'center', gap:10 }}>
        <span className="eyebrow" style={{ fontSize:9 }}>{city.name.toUpperCase()} CITY CENTRE · 9 CM · 1:11000</span>
      </div>
      <CornerMarks/>
    </div>
  );
}

/* Hero variant B — multi-photo collage */
function HeroPhotoStack({ city }) {
  const photos = city.photos || [city.on_base, city.photo].filter(Boolean);
  const [active, setActive] = React.useState(0);
  return (
    <div style={{ position:'relative', height:700, display:'grid', gridTemplateRows:'1fr 110px', gap:14 }}>
      <div style={{ position:'relative', border:'1px solid var(--ink)', overflow:'hidden', background:'var(--bone)' }}>
        {photos.map((src, i) => (
          <img key={src} src={src} alt={city.name}
            className="img-cinema"
            style={{
              position:'absolute', inset:0, width:'100%', height:'100%', objectFit:'cover',
              opacity: i === active ? 1 : 0,
              transition: 'opacity .5s ease',
            }}/>
        ))}
        <div style={{ position:'absolute', top:18, left:18,
          background:'var(--bone)', border:'1px solid var(--ink)', padding:'8px 12px' }}>
          <span className="eyebrow" style={{ fontSize:9 }}>SHEFFIELD · FIG. {String(active + 1).padStart(2,'0')} OF {String(photos.length).padStart(2,'0')}</span>
        </div>
        <CornerMarks/>
      </div>
      <div style={{ display:'grid', gridTemplateColumns:`repeat(${Math.min(photos.length, 6)}, 1fr)`, gap:8 }}>
        {photos.slice(0, 6).map((src, i) => (
          <button key={src} onMouseEnter={() => setActive(i)} onFocus={() => setActive(i)}
            style={{ display:'block', border:`1px solid ${i === active ? 'var(--ink)' : 'var(--line)'}`,
              background:'var(--bone)', overflow:'hidden', height:'100%' }}>
            <img src={src} alt="" className="img-editorial"
              style={{ width:'100%', height:'100%', objectFit:'cover',
                opacity: i === active ? 1 : 0.85 }}/>
          </button>
        ))}
      </div>
    </div>
  );
}

/* Hero variant C — plan view */
function HeroPlan({ city }) {
  return (
    <div style={{ position:'relative', height:700, border:'1px solid var(--ink)', overflow:'hidden', background:'var(--bone)' }}>
      <StyledMap seed={city.id + '-plan'} bbox={{ x:30, y:30, w:38, h:38, r:0 }}/>
      <div style={{ position:'absolute', top:18, left:18,
        background:'var(--bone)', border:'1px solid var(--ink)', padding:'10px 14px' }}>
        <div className="eyebrow" style={{ fontSize:9 }}>PLAN · 1.00 × 1.00 KM</div>
        <div style={{ marginTop:6, fontSize:13, fontWeight:600 }}>{city.name} · City Centre</div>
      </div>
      <CornerMarks/>
    </div>
  );
}

/* Hero variant D — triptych: photo + plan + render */
function HeroTriptych({ city }) {
  const main = city.on_base || (city.photos && city.photos[0]) || city.photo;
  const detail = (city.photos && city.photos[2]) || city.photo;
  return (
    <div style={{ height:700, display:'grid', gridTemplateRows:'1.4fr 1fr', gap:14 }}>
      <div style={{ position:'relative', border:'1px solid var(--ink)', overflow:'hidden', background:'var(--bone)' }}>
        <img src={main} alt={city.name} className="img-cinema"
          style={{ width:'100%', height:'100%', objectFit:'cover' }}/>
        <div style={{ position:'absolute', top:18, left:18, background:'var(--bone)', border:'1px solid var(--ink)', padding:'8px 12px' }}>
          <span className="eyebrow" style={{ fontSize:9 }}>01 · OBJECT</span>
        </div>
      </div>
      <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:14 }}>
        <div style={{ position:'relative', border:'1px solid var(--ink)', overflow:'hidden', background:'var(--bone)' }}>
          <StyledMap seed={city.id + '-tri'} bbox={{ x:30, y:30, w:38, h:38, r:0 }}/>
          <div style={{ position:'absolute', top:14, left:14, background:'var(--bone)', border:'1px solid var(--ink)', padding:'5px 10px' }}>
            <span className="eyebrow" style={{ fontSize:9 }}>02 · PLAN</span>
          </div>
        </div>
        <div style={{ position:'relative', border:'1px solid var(--ink)', overflow:'hidden', background:'var(--bone)' }}>
          <img src={detail} alt="" className="img-cinema"
            style={{ width:'100%', height:'100%', objectFit:'cover' }}/>
          <div style={{ position:'absolute', top:14, left:14, background:'var(--bone)', border:'1px solid var(--ink)', padding:'5px 10px' }}>
            <span className="eyebrow" style={{ fontSize:9 }}>03 · DETAIL</span>
          </div>
        </div>
      </div>
    </div>
  );
}

/* ────────────────────────────── 02 MARQUEE */
function MarqueeStripV2() {
  const items = [
    'EA LIDAR · 1 M RESOLUTION',
    'OPENSTREETMAP FOOTPRINTS',
    'PETG · MIST · BAMBU LAB X1',
    'MADE IN SHEFFIELD',
    'CITYFORM.CO · ETSY/CITYFORM',
    'FREE UK SHIPPING OVER £75',
    'ONE FORMAT · 9 CM SQUARE',
    'TWELVE CITIES · ONE ATLAS',
    'COMMISSIONS WELCOME',
  ];
  const Row = () => (
    <div className="marquee-row">
      {items.map((s, i) => (
        <span key={i} style={{ display:'inline-flex', alignItems:'center', gap:64, fontSize:11, letterSpacing:'0.32em', fontWeight:500, color:'var(--ink-soft)' }}>
          {s}
          <span style={{ width:6, height:6, background:'var(--ink)', display:'inline-block' }}/>
        </span>
      ))}
    </div>
  );
  return (
    <div style={{ background:'var(--bone)', borderBottom:'1px solid var(--line)', borderTop:'1px solid var(--line)', overflow:'hidden', padding:'18px 0' }}>
      <div className="marquee-track">
        <Row/>
        <Row/>
      </div>
    </div>
  );
}

/* ────────────────────────────── 03 SIGNATURE PROCESS — scroll-driven scene */
function SignatureProcess({ mode = 'scroll' }) {
  // mode: 'scroll' (the user scrolls to advance), 'auto' (cycles on a timer
  // for marketing demos), 'off' (renders a flat overview, no animation).
  const [ref, scrollP] = useScrollProgress();
  const [autoP, setAutoP] = React.useState(0);
  React.useEffect(() => {
    if (mode !== 'auto') return;
    let raf, t0 = performance.now();
    const tick = (t) => {
      const dt = (t - t0) / 1000;
      const cycle = 18; // sec per full cycle
      setAutoP(((dt % cycle) / cycle));
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [mode]);
  const p = mode === 'scroll' ? scrollP : mode === 'auto' ? autoP : 1;

  const phaseProgress = (i) => {
    const start = i / 4, end = (i + 1) / 4;
    if (p < start - 0.06) return 0;
    if (p > end + 0.06) return 0;
    if (p < start) return (p - (start - 0.06)) / 0.06;
    if (p > end) return 1 - (p - end) / 0.06;
    return 1;
  };
  const activePhase = mode === 'off' ? 3 : Math.min(3, Math.floor(p * 4 + 0.001));
  const heightVh = mode === 'scroll' ? 360 : 100;

  const phases = [
    {
      n: '01', title: 'Survey',
      kicker: 'ENVIRONMENT AGENCY · LIDAR',
      body: 'A 1 m resolution scan of the ground and everything on it. Released by the EA under the Open Government Licence. We crop a single square kilometre.',
      side: '47.3 MB · DTM + DSM',
    },
    {
      n: '02', title: 'Footprints',
      kicker: 'OPENSTREETMAP · BUILDINGS, WATER',
      body: 'OSM gives us the polygons: roof outlines, water boundaries, bridges. Each shape is closed and oriented before it meets the mesh.',
      side: 'overpass-turbo · 1.04 sec',
    },
    {
      n: '03', title: 'Mesh',
      kicker: 'WATERTIGHT · MEASURED ROOF SURFACES',
      body: 'The LIDAR heightfield and OSM footprints are fused into a single watertight mesh. Water is cut down by 1 mm to read as river, not pavement.',
      side: 'tier-3 · ~3.2 M tris',
    },
    {
      n: '04', title: 'Object',
      kicker: 'BAMBU LAB X1 · PETG MIST · 9 CM',
      body: 'Printed in PETG Mist, hand-finished, seated on a graphite base. Boxed in a kraft mailer with attribution and an off-print on the inside cover.',
      side: '7 days · made to order',
    },
  ];

  const sectionContent = (
    <div className="container" style={{
      height: mode === 'scroll' ? '100%' : 'auto',
      display:'grid', gridTemplateColumns:'1fr 1fr', gap:60, alignItems:'center',
      position:'relative', paddingTop:64, paddingBottom:64,
    }}>
      <div className="coord-grid" style={{ opacity:0.35 }}/>

      <div style={{ position:'relative', zIndex:2 }}>
        <Reveal kind="reveal" style={{ marginBottom:32, display:'flex', alignItems:'center', gap:14 }}>
          <span style={{ width:48, height:1, background:'var(--ink)' }}/>
          <span className="eyebrow" style={{ fontSize:10 }}>THE PROCESS · IN FOUR ACTS</span>
        </Reveal>
        <Reveal kind="reveal" delay={120}>
          <h2 className="display-section" style={{ fontSize:'clamp(56px, 6vw, 86px)', marginBottom:32 }}>
            How a place<br/>becomes a shelf object.
          </h2>
        </Reveal>

        <div style={{ borderTop:'1px solid var(--ink)' }}>
          {phases.map((ph, i) => {
            const active = i === activePhase || mode === 'off';
            return (
              <div key={i} style={{
                padding:'22px 0',
                borderBottom:'1px solid var(--line)',
                transition: 'opacity .4s, transform .5s cubic-bezier(.2,.6,.2,1)',
                opacity: mode === 'off' ? 1 : active ? 1 : 0.32,
                display:'grid', gridTemplateColumns:'70px 1fr 130px', gap:18, alignItems:'baseline',
              }}>
                <div className="tnum" style={{ fontSize:22, fontWeight:600, letterSpacing:'-0.01em' }}>{ph.n}</div>
                <div>
                  <div style={{ fontSize:24, fontWeight:600, letterSpacing:'-0.01em' }}>{ph.title}</div>
                  <div className="eyebrow" style={{ marginTop:8, fontSize:9 }}>{ph.kicker}</div>
                  {(active || mode === 'off') && (
                    <p className="body" style={{ marginTop:14, fontSize:14.5, lineHeight:1.6, color:'var(--ink-soft)', maxWidth:480 }}>
                      {ph.body}
                    </p>
                  )}
                </div>
                <div style={{ textAlign:'right' }}>
                  <span className="tnum" style={{ fontSize:11, color:'var(--stone)' }}>{ph.side}</span>
                </div>
              </div>
            );
          })}
        </div>

        {mode === 'scroll' && (
          <div style={{ marginTop:32, position:'relative' }}>
            <div className="eyebrow" style={{ marginBottom:10 }}>SCROLL PROGRESS</div>
            <div style={{ height:2, background:'var(--line)', position:'relative' }}>
              <div style={{
                position:'absolute', left:0, top:0, bottom:0,
                width:`${p * 100}%`, background:'var(--ink)',
                transition: 'width .15s linear',
              }}/>
              {[0.25, 0.5, 0.75].map(x => (
                <div key={x} style={{ position:'absolute', left:`${x*100}%`, top:-3, width:1, height:8, background:'var(--ink)', opacity:0.4 }}/>
              ))}
            </div>
            <div style={{ display:'flex', justifyContent:'space-between', marginTop:8 }}>
              {phases.map((ph, i) => (
                <span key={i} className="eyebrow" style={{ fontSize:9, color: i === activePhase ? 'var(--ink)' : 'var(--stone)' }}>{ph.n}</span>
              ))}
            </div>
          </div>
        )}
      </div>

      <ProcessStage progress={p} phaseProgress={phaseProgress} activePhase={activePhase} mode={mode}/>
    </div>
  );

  return (
    <section ref={ref} style={{ background:'var(--mist)', position:'relative', borderBottom:'1px solid var(--line)' }}>
      {mode === 'scroll' ? (
        <div style={{ height:`${heightVh}vh`, position:'relative' }}>
          <div className="scene-pin">{sectionContent}</div>
        </div>
      ) : sectionContent}
    </section>
  );
}

function ProcessStage({ progress, phaseProgress, activePhase, mode }) {
  const lidarDots = React.useMemo(() => {
    const rng = window.makeRng('sheffield-lidar-v2');
    const cells = [];
    const N = 36;
    for (let y = 0; y < N; y++) {
      for (let x = 0; x < N; x++) {
        const cx = (x - N/2) / N, cy = (y - N/2) / N;
        const h1 = Math.exp(-(cx*cx + cy*cy) * 5);
        const h2 = Math.exp(-((cx-0.2)**2 + (cy+0.15)**2) * 12);
        const h3 = rng() * 0.25;
        cells.push({ x: x / N, y: y / N, h: Math.max(0, Math.min(1, h1 * 0.7 + h2 * 0.4 + h3)) });
      }
    }
    return cells;
  }, []);

  const tile = React.useMemo(() => window.generateTile('sheffield-process'), []);
  const photoSrc = 'assets/products/sheffield_on_base.png';

  return (
    <div style={{ position:'relative', alignSelf:'stretch', height:'100%', display:'flex', alignItems:'center', justifyContent:'center' }}>
      <div style={{ position:'relative', width:'100%', aspectRatio:'1/1', maxWidth:560 }}>
        {/* 01 LIDAR */}
        <div style={{ position:'absolute', inset:0, background:'var(--bone)', border:'1px solid var(--ink)',
          opacity: phaseProgress(0), transition: 'opacity .4s ease' }}>
          <svg viewBox="0 0 1000 1000" style={{ width:'100%', height:'100%' }}>
            <rect x="0" y="0" width="1000" height="1000" fill="var(--bone)"/>
            {lidarDots.map((c, i) => {
              const sx = c.x * 1000 + 14, sy = c.y * 1000 + 14;
              const size = 4 + c.h * 14, shade = 0.15 + c.h * 0.7;
              return <circle key={i} cx={sx} cy={sy} r={size / 2} fill={`rgba(26,31,38,${shade.toFixed(2)})`}/>;
            })}
            <line x1="0" y1={progress * 4 * 1000} x2="1000" y2={progress * 4 * 1000}
              stroke="var(--ink)" strokeWidth="1" opacity={0.4}/>
          </svg>
          <StageBadge text="01 · LIDAR HEIGHTFIELD" sub="1 M RESOLUTION · DTM + DSM"/>
          <StageMeasure top="20%" left="14%" v="34.6m"/>
          <StageMeasure top="60%" left="40%" v="46.2m"/>
        </div>

        {/* 02 Footprints */}
        <div style={{ position:'absolute', inset:0, background:'var(--mist)', border:'1px solid var(--ink)',
          opacity: phaseProgress(1), transition: 'opacity .4s ease' }}>
          <svg viewBox="0 0 1000 1000" style={{ width:'100%', height:'100%' }}>
            <rect x="0" y="0" width="1000" height="1000" fill="var(--mist)"/>
            <g opacity="0.18">
              {lidarDots.filter((_, i) => i % 4 === 0).map((c, i) => (
                <circle key={i} cx={c.x * 1000 + 14} cy={c.y * 1000 + 14} r="3" fill="var(--ink)"/>
              ))}
            </g>
            <g>
              {tile.roads.map((r, i) => (
                <path key={'rb'+i} d={r.d} fill="none" stroke="var(--ink)" strokeWidth={r.w + 1.5} opacity="0.4"/>
              ))}
              {tile.roads.map((r, i) => (
                <path key={'rf'+i} d={r.d} fill="none" stroke="var(--bone)" strokeWidth={r.w} strokeLinecap="round"/>
              ))}
            </g>
            <g fill="none" stroke="var(--ink)" strokeWidth="1.2">
              {tile.buildings.map((b, i) => (
                <rect key={i} x={b.x} y={b.y} width={b.w} height={b.h}
                  transform={b.r ? `rotate(${b.r} ${b.x + b.w/2} ${b.y + b.h/2})` : undefined}/>
              ))}
            </g>
            {tile.waterPath && <path d={tile.waterPath} fill="none" stroke="#9BA09B" strokeWidth="40"/>}
          </svg>
          <StageBadge text="02 · OSM FOOTPRINTS" sub="BUILDINGS · WATER · BRIDGES"/>
          <StageMeasure top="22%" right="14%" v="1.04 sec"/>
        </div>

        {/* 03 Mesh */}
        <div style={{ position:'absolute', inset:0, background:'#0F1216', border:'1px solid var(--ink)',
          opacity: phaseProgress(2), transition: 'opacity .4s ease' }}>
          <svg viewBox="0 0 1000 1000" style={{ width:'100%', height:'100%' }}>
            <g transform="translate(500,560) rotate(-20) skewX(-12) scale(1, 0.62)">
              <rect x="-400" y="-400" width="800" height="800" fill="#1A2026" stroke="#3D4450" strokeWidth="2"/>
              <g stroke="#3D4450" strokeWidth="0.5">
                {Array.from({length: 17}).map((_, i) => (
                  <React.Fragment key={'g'+i}>
                    <line x1={-400 + i*50} y1="-400" x2={-400 + i*50} y2="400"/>
                    <line x1="-400" y1={-400 + i*50} x2="400" y2={-400 + i*50}/>
                  </React.Fragment>
                ))}
              </g>
              {tile.buildings.slice(0, 26).map((b, i) => {
                const ex = (b.x - 500) * 0.8;
                const ey = (b.y - 500) * 0.8;
                const h = 30 + (i % 5) * 18;
                return (
                  <g key={i}>
                    <rect x={ex} y={ey - h} width={b.w * 0.8} height={h} fill="#2A3038" stroke="#C0C4BD" strokeWidth="0.5"/>
                    <rect x={ex} y={ey} width={b.w * 0.8} height={b.h * 0.8} fill="#1A2026" stroke="#C0C4BD" strokeWidth="0.5"/>
                  </g>
                );
              })}
            </g>
            <g stroke="#C0C4BD" strokeWidth="0.4" fill="none" opacity={0.5 + 0.3 * Math.sin(progress * 14)}>
              <line x1="0" y1="200" x2="1000" y2="200"/>
              <line x1="0" y1="400" x2="1000" y2="400"/>
              <line x1="0" y1="600" x2="1000" y2="600"/>
              <line x1="0" y1="800" x2="1000" y2="800"/>
            </g>
          </svg>
          <StageBadge text="03 · WATERTIGHT MESH" sub="~3.2 M TRIANGLES · TIER-3" dark/>
          <StageMeasure top="22%" left="14%" v="3.2M tris" dark/>
        </div>

        {/* 04 Object — real Sheffield on-base photo */}
        <div style={{ position:'absolute', inset:0, background:'var(--mist)', border:'1px solid var(--ink)', overflow:'hidden',
          opacity: phaseProgress(3), transition: 'opacity .4s ease' }}>
          <img src={photoSrc} alt="finished object"
            style={{
              width:'100%', height:'100%', objectFit:'contain',
              padding: '6%', background: 'var(--mist)',
              transform: `scale(${1.0 + (1 - phaseProgress(3)) * 0.04})`,
              transition: 'transform .6s ease',
            }}/>
          <StageBadge text="04 · FINISHED OBJECT" sub="9 CM · PETG MIST · MADE IN SHEFFIELD"/>
        </div>

        <CornerMarks/>
      </div>
    </div>
  );
}

function StageBadge({ text, sub, dark }) {
  return (
    <div style={{
      position:'absolute', top:20, left:20,
      background: dark ? '#0F1216' : 'var(--bone)',
      color: dark ? 'var(--mist)' : 'var(--ink)',
      border:`1px solid ${dark ? 'var(--mist)' : 'var(--ink)'}`,
      padding:'8px 14px',
    }}>
      <div className="eyebrow" style={{ fontSize:9, color: dark ? 'rgba(225,229,224,0.7)' : 'var(--stone)' }}>{sub}</div>
      <div style={{ marginTop:4, fontSize:12, fontWeight:600, letterSpacing:'-0.005em' }}>{text}</div>
    </div>
  );
}
function StageMeasure({ top, left, right, v, dark }) {
  return (
    <div style={{ position:'absolute', top, left, right, transform: 'translate(-50%, -50%)', width:0, height:0 }}>
      <div style={{
        position:'absolute', whiteSpace:'nowrap', left: 8, top: -8,
        padding:'3px 8px',
        background: dark ? 'rgba(15,18,22,0.8)' : 'var(--bone)',
        color: dark ? 'var(--mist)' : 'var(--ink)',
        border:`1px solid ${dark ? 'var(--mist)' : 'var(--ink)'}`,
        fontSize:9, letterSpacing:'0.06em', fontVariantNumeric:'tabular-nums',
      }}>
        <span style={{ position:'absolute', left:-6, top:'50%', width:8, height:1, background: dark ? 'var(--mist)' : 'var(--ink)' }}/>
        {v}
      </div>
      <span style={{ position:'absolute', left:-3, top:-3, width:6, height:6, background: dark ? 'var(--mist)' : 'var(--ink)' }}/>
    </div>
  );
}

/* ────────────────────────────── 04 FEATURED COLLECTION */
function FeaturedCollection({ items, go }) {
  return (
    <section style={{ background:'var(--bone)', borderBottom:'1px solid var(--line)' }}>
      <div className="container" style={{ paddingTop:120, paddingBottom:96 }}>
        <div style={{ display:'flex', justifyContent:'space-between', alignItems:'baseline', marginBottom:52 }}>
          <Reveal>
            <div className="eyebrow" style={{ marginBottom:18 }}>FROM THE COLLECTION</div>
            <h2 className="display-section" style={{ fontSize:'clamp(56px, 6vw, 84px)' }}>
              Six cities,<br/>six signatures.
            </h2>
          </Reveal>
          <Reveal delay={200} style={{ maxWidth:340 }}>
            <p className="body" style={{ fontSize:15, lineHeight:1.6, color:'var(--ink-soft)' }}>
              The twelve are paired in six: a city, a landmark, the colour the landmark gives back. Same square, same scale.
            </p>
            <button className="btn-v2-ghost btn-v2" style={{ marginTop:20 }} onClick={() => go('atlas')}>
              See all twelve <span className="btn-arrow">{Icon.arrow}</span>
            </button>
          </Reveal>
        </div>
        <div style={{ display:'grid', gridTemplateColumns:'repeat(3, 1fr)', gap:24 }}>
          {items.slice(0, 6).map((item, i) => (
            <Reveal key={item.id} delay={i * 80}>
              <ProductCard item={item} onClick={() => go('product', item.id)}/>
            </Reveal>
          ))}
        </div>
      </div>
    </section>
  );
}

/* ────────────────────────────── 05 THE TWELVE
   Replaces the sticker wall. Twelve photographed objects, in a grid,
   on a dark canvas. The accent colour shows up only as a tiny rule. */
function TheTwelveGrid({ go }) {
  const twelve = window.CATALOG.filter(c => c.featured).slice(0, 12);
  return (
    <section style={{ background:'var(--ink)', color:'var(--mist)', position:'relative', overflow:'hidden' }}>
      <div className="container" style={{ paddingTop:120, paddingBottom:120, position:'relative' }}>
        <div style={{ textAlign:'center', marginBottom:72 }}>
          <Reveal>
            <div className="eyebrow" style={{ color:'rgba(225,229,224,0.55)', fontSize:10, marginBottom:18 }}>THE TWELVE</div>
            <h2 className="display-section" style={{ fontSize:'clamp(56px, 6vw, 96px)', color:'var(--mist)' }}>
              One format. Twelve places.
            </h2>
            <p className="body" style={{ fontSize:16, color:'rgba(225,229,224,0.7)', maxWidth:560, margin:'24px auto 0', lineHeight:1.6 }}>
              Six in production, six in development. Each is a 9&nbsp;cm crop, scaled to the same 1:11000, printed in PETG&nbsp;Mist and seated on a graphite base.
            </p>
          </Reveal>
        </div>
        <div style={{ display:'grid', gridTemplateColumns:'repeat(4, 1fr)', gap:24 }}>
          {twelve.map((c, i) => (
            <Reveal key={c.id} delay={i * 50}>
              <button onClick={() => go('product', c.id)} className="card-lift"
                style={{ display:'block', width:'100%', textAlign:'left', color:'var(--mist)' }}>
                <div style={{
                  aspectRatio:'4/5', overflow:'hidden', position:'relative',
                  background:'#0F1216', border:'1px solid rgba(225,229,224,0.18)',
                }}>
                  {(c.on_base || c.photo) ? (
                    <img src={c.on_base || c.photo} alt={c.name}
                      style={{ width:'100%', height:'100%', objectFit:'cover',
                        filter:'grayscale(0.35) contrast(1.05) brightness(0.96)' }}/>
                  ) : (
                    <div style={{ width:'100%', height:'100%', display:'flex', alignItems:'center', justifyContent:'center' }}>
                      <span className="eyebrow" style={{ color:'rgba(225,229,224,0.45)', fontSize:9 }}>AWAITING</span>
                    </div>
                  )}
                  {c.status === 'soon' && (
                    <div style={{
                      position:'absolute', top:12, left:12,
                      background:'var(--mist)', color:'var(--ink)',
                      padding:'4px 10px', fontSize:9, letterSpacing:'0.22em', fontWeight:500,
                    }}>IN DEVELOPMENT</div>
                  )}
                  {/* Accent hairline at top — the ONLY place the city colour shows here */}
                  {c.accent && (
                    <div style={{ position:'absolute', top:0, left:0, right:0, height:2, background:`var(--${c.accent})` }}/>
                  )}
                </div>
                <div style={{ marginTop:18, display:'flex', justifyContent:'space-between', alignItems:'baseline' }}>
                  <div>
                    <div style={{ fontSize:20, fontWeight:600, letterSpacing:'-0.01em' }}>{c.name}</div>
                    <div className="tnum" style={{ marginTop:6, fontSize:10, color:'rgba(225,229,224,0.55)' }}>
                      {c.coords.lat.toFixed(3)}°{c.coords.ns} · {Math.abs(c.coords.lng).toFixed(3)}°{c.coords.ew}
                    </div>
                  </div>
                  <span className="tnum" style={{ fontSize:11, color:'rgba(225,229,224,0.7)' }}>
                    {String(i + 1).padStart(2,'0')}
                  </span>
                </div>
              </button>
            </Reveal>
          ))}
        </div>
      </div>
    </section>
  );
}

/* ────────────────────────────── 06 COMMISSION PREVIEW */
function CommissionPreviewV2({ go }) {
  return (
    <section style={{ background:'var(--mist)', position:'relative', overflow:'hidden', borderBottom:'1px solid var(--line)' }}>
      <OsContourBg seed="commission-bg" density={9} opacity={0.12}/>
      <div className="container" style={{ paddingTop:120, paddingBottom:120, position:'relative', zIndex:2 }}>
        <div style={{ display:'grid', gridTemplateColumns:'1fr 1.1fr', gap:80, alignItems:'center' }}>
          <Reveal>
            <div style={{ display:'flex', alignItems:'center', gap:14, marginBottom:24 }}>
              <span style={{ width:48, height:1, background:'var(--ink)' }}/>
              <span className="eyebrow" style={{ fontSize:10 }}>COMMISSION</span>
            </div>
            <h2 className="display-section" style={{ fontSize:'clamp(56px, 6.5vw, 100px)', marginBottom:32 }}>
              Your place,<br/>your square kilometre.
            </h2>
            <p className="body" style={{ fontSize:17, lineHeight:1.6, color:'var(--ink-soft)', maxWidth:500 }}>
              The childhood street. The summit you've climbed twenty times. The hospital where they were born. Anywhere on the map, on the same system as the twelve.
            </p>
            <div style={{ marginTop:36, display:'grid', gap:18 }}>
              {[
                ['Free preview',   'See your area in plan view before you spend a penny.'],
                ['Same system',    '9 × 9 cm, 1:11000, PETG Mist on a graphite base.'],
                ['Eight-week turnaround', 'Printed, finished, photographed and shipped from Sheffield.'],
              ].map(([title, d]) => (
                <div key={title} style={{ display:'flex', gap:16, alignItems:'flex-start' }}>
                  <span style={{ marginTop:4, color:'var(--ink)' }}>{Icon.check}</span>
                  <div>
                    <div style={{ fontWeight:600, fontSize:15.5 }}>{title}</div>
                    <div className="body" style={{ fontSize:14, color:'var(--stone)', marginTop:2 }}>{d}</div>
                  </div>
                </div>
              ))}
            </div>
            <button className="btn-v2" style={{ marginTop:40 }} onClick={() => go('atelier')}>
              Start with a postcode <span className="btn-arrow">{Icon.arrow}</span>
            </button>
          </Reveal>

          <Reveal kind="reveal-clip" delay={200}>
            <div style={{ position:'relative', border:'1px solid var(--ink)', height:560, background:'var(--bone)' }}>
              <StyledMap seed="sheffield-preview" bbox={{ x:30, y:30, w:38, h:38, r:0 }}/>
              <div style={{ position:'absolute', top:22, left:22, right:22, display:'flex', alignItems:'center', gap:10,
                padding:'14px 18px', background:'var(--bone)', border:'1px solid var(--ink)' }}>
                {Icon.search}
                <span style={{ fontSize:14, fontWeight:500 }}>S1 2HE — Sheffield City Hall</span>
                <span style={{ marginLeft:'auto' }} className="caption">53.3811° N · 1.4701° W</span>
              </div>
              <div style={{ position:'absolute', bottom:22, left:22, right:22, padding:'14px 18px',
                background:'var(--bone)', border:'1px solid var(--ink)',
                display:'flex', alignItems:'center', justifyContent:'space-between' }}>
                <div>
                  <div className="eyebrow" style={{ fontSize:9 }}>PREVIEW AREA</div>
                  <div style={{ fontSize:14, fontWeight:600, marginTop:6, letterSpacing:'-0.005em' }}>Sheffield City Centre · 1.00 × 1.00 km</div>
                </div>
                <button className="btn-v2" style={{ height:40, padding:'0 16px', fontSize:11.5 }} onClick={() => go('atelier')}>
                  Open picker <span className="btn-arrow">{Icon.arrow}</span>
                </button>
              </div>
              <CornerMarks/>
            </div>
          </Reveal>
        </div>
      </div>
    </section>
  );
}

/* ────────────────────────────── 07 AR SHELF MODULE
   Surfaces the existing AR Quick Look feature (USDZ at /api/ar/usdz/<id>.usdz)
   for visitors who haven't entered the commission flow yet. iPhone Camera
   → scan → AR preview at true 9 cm. Falls back to a tasteful "iPhone
   required" note when the API isn't reachable. */
function ARShelfModule() {
  const [supported, setSupported] = React.useState(null); // null = probing
  const [arUrl, setArUrl] = React.useState(null);

  React.useEffect(() => {
    // Probe the sample USDZ endpoint. In production the page knows which
    // output_id to load; for the marketing module we pick the most recent
    // commissioned model from the cache. The endpoint returns a redirect
    // or a file; either is fine.
    const sampleId = 'sample';
    const url = window.location.origin + '/api/ar/usdz/' + sampleId + '.usdz';
    fetch(url, { method: 'HEAD' })
      .then(r => { setSupported(r.ok); if (r.ok) setArUrl(url); })
      .catch(() => setSupported(false));
  }, []);

  // Build a static QR for the sample (or generic info card) — we DO have
  // /api/qr in production but for the marketing surface a plain SVG QR
  // placeholder lets us render even without backend.
  const qr = (
    <svg viewBox="0 0 33 33" width="100%" height="100%" shapeRendering="crispEdges">
      <rect width="33" height="33" fill="var(--bone)"/>
      {/* synthetic QR pattern — visually convincing but not scannable.
          When supported is true and arUrl is set, we swap for /api/qr. */}
      {Array.from({ length: 33 * 33 }).map((_, i) => {
        const x = i % 33, y = Math.floor(i / 33);
        const seed = (x * 71 + y * 113 + x * y * 17) % 7;
        if ((x < 7 && y < 7) || (x > 25 && y < 7) || (x < 7 && y > 25)) {
          const inFinder = (x >= 1 && x <= 5 && y >= 1 && y <= 5);
          const innerDot = (x >= 2 && x <= 4 && y >= 2 && y <= 4);
          if (inFinder && !innerDot) return null;
          if ((x === 0 || x === 6 || y === 0 || y === 6) && (x < 7 && y < 7)) return <rect key={i} x={x} y={y} width="1" height="1" fill="var(--ink)"/>;
          if (innerDot) return <rect key={i} x={x} y={y} width="1" height="1" fill="var(--ink)"/>;
          if ((x === 26 || x === 32 || y === 0 || y === 6) && (x > 25 && y < 7)) return <rect key={i} x={x} y={y} width="1" height="1" fill="var(--ink)"/>;
          if ((x >= 28 && x <= 30 && y >= 2 && y <= 4)) return <rect key={i} x={x} y={y} width="1" height="1" fill="var(--ink)"/>;
          if ((x === 0 || x === 6 || y === 26 || y === 32) && (x < 7 && y > 25)) return <rect key={i} x={x} y={y} width="1" height="1" fill="var(--ink)"/>;
          if ((x >= 2 && x <= 4 && y >= 28 && y <= 30)) return <rect key={i} x={x} y={y} width="1" height="1" fill="var(--ink)"/>;
          return null;
        }
        return seed < 2 ? <rect key={i} x={x} y={y} width="1" height="1" fill="var(--ink)"/> : null;
      })}
    </svg>
  );

  return (
    <section style={{ background:'var(--bone)', borderTop:'1px solid var(--line)', borderBottom:'1px solid var(--line)' }}>
      <div className="container" style={{ paddingTop:108, paddingBottom:108 }}>
        <div style={{ display:'grid', gridTemplateColumns:'1.1fr 1fr', gap:80, alignItems:'center' }}>
          <Reveal>
            <div style={{ display:'flex', alignItems:'center', gap:14, marginBottom:22 }}>
              <span style={{ width:48, height:1, background:'var(--ink)' }}/>
              <span className="eyebrow" style={{ fontSize:10 }}>AR · NO APP REQUIRED</span>
            </div>
            <h2 className="display-section" style={{ fontSize:'clamp(56px, 6.2vw, 96px)', marginBottom:28 }}>
              See it on your shelf,<br/>before you order.
            </h2>
            <p className="body" style={{ fontSize:17, lineHeight:1.6, color:'var(--ink-soft)', maxWidth:520 }}>
              Scan with the iPhone Camera. Safari opens AR Quick Look and places the model at <strong>true 9&nbsp;cm</strong> on any flat surface. Walk around it. Move it. See whether it lives on the mantelpiece or the desk.
            </p>
            <div style={{ marginTop:36, display:'grid', gridTemplateColumns:'repeat(3, 1fr)', gap:18 }}>
              {[
                ['01', 'Point Camera', 'Open Camera, point at the code.'],
                ['02', 'Tap to open',  'Safari opens the AR preview.'],
                ['03', 'Place it',     'Tap on a surface to place at 9 cm.'],
              ].map(([n, t, d]) => (
                <div key={n} style={{ borderTop:'1px solid var(--ink)', paddingTop:14 }}>
                  <div className="tnum" style={{ fontSize:11, fontWeight:600, letterSpacing:'0.18em' }}>{n}</div>
                  <div style={{ marginTop:6, fontWeight:600, fontSize:15 }}>{t}</div>
                  <div className="body" style={{ marginTop:6, fontSize:12.5, color:'var(--stone)', lineHeight:1.55 }}>{d}</div>
                </div>
              ))}
            </div>
            <div style={{ marginTop:32, padding:'14px 18px', background:'var(--mist)', borderLeft:'2px solid var(--ink)',
              fontSize:12.5, color:'var(--ink)', lineHeight:1.55 }}>
              <strong>iPhone only.</strong> Android AR Core in development &mdash; on Android the same scan opens a high-res 3D preview, no AR placement yet.
            </div>
          </Reveal>

          <Reveal kind="reveal-clip" delay={200}>
            <div style={{ position:'relative', display:'flex', alignItems:'center', justifyContent:'center', minHeight:600 }}>
              {/* Phone mockup */}
              <div style={{
                width:280, aspectRatio:'9/19', position:'relative',
                background:'#0F1216', borderRadius:36, padding:10,
                boxShadow:'0 50px 80px -40px rgba(0,0,0,0.4)',
              }}>
                <div style={{
                  width:'100%', height:'100%', borderRadius:26,
                  background:'var(--mist)', position:'relative', overflow:'hidden',
                }}>
                  <img src="assets/products/sheffield_on_base.png" alt="AR preview"
                    style={{ width:'100%', height:'100%', objectFit:'cover' }}/>
                  {/* AR overlay */}
                  <div style={{ position:'absolute', inset:0, display:'flex', flexDirection:'column', justifyContent:'space-between', padding:14 }}>
                    <div style={{ display:'flex', justifyContent:'space-between' }}>
                      <span style={{ background:'rgba(15,18,22,0.7)', color:'#fff', fontSize:9, letterSpacing:'0.22em', fontWeight:500, padding:'3px 8px' }}>AR · LIVE</span>
                      <span style={{ background:'rgba(15,18,22,0.7)', color:'#fff', fontSize:9, fontWeight:500, padding:'3px 8px', fontVariantNumeric:'tabular-nums' }}>9.0 CM</span>
                    </div>
                    {/* Plane indicator */}
                    <div style={{ position:'absolute', left:'50%', top:'62%', transform:'translate(-50%, -50%)' }}>
                      <svg viewBox="0 0 200 200" width="160" height="160" style={{ opacity:0.8 }}>
                        <ellipse cx="100" cy="100" rx="90" ry="20" fill="none" stroke="#fff" strokeWidth="1.5" strokeDasharray="6 4"/>
                        <ellipse cx="100" cy="100" rx="70" ry="14" fill="none" stroke="#fff" strokeWidth="1" opacity="0.6"/>
                        <circle cx="100" cy="100" r="2" fill="#fff"/>
                      </svg>
                    </div>
                    <div style={{ display:'flex', justifyContent:'center', gap:14 }}>
                      <span style={{ width:38, height:38, borderRadius:'50%', background:'rgba(255,255,255,0.92)', display:'flex', alignItems:'center', justifyContent:'center' }}>
                        <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="#000" strokeWidth="1.8"><path d="M12 5v14M5 12h14"/></svg>
                      </span>
                    </div>
                  </div>
                </div>
              </div>

              {/* QR card to the left */}
              <div style={{
                position:'absolute', left:'4%', bottom:'18%',
                background:'var(--mist)', border:'1px solid var(--ink)',
                padding:14, width:188,
              }}>
                <div className="eyebrow" style={{ fontSize:9 }}>SCAN · TO SEE IT</div>
                <div style={{ marginTop:10, width:'100%', aspectRatio:'1/1' }}>
                  {arUrl ? (
                    <img src={'/api/qr?data=' + encodeURIComponent(arUrl)} alt="AR QR"
                      style={{ width:'100%', height:'100%' }}/>
                  ) : qr}
                </div>
                <div className="tnum" style={{ marginTop:10, fontSize:9, color:'var(--stone)', letterSpacing:'0.18em' }}>
                  SHEFFIELD · 9 CM
                </div>
              </div>

              {/* Floating measurement annotations */}
              <div style={{ position:'absolute', right:'2%', top:'14%',
                background:'var(--bone)', border:'1px solid var(--ink)', padding:'10px 14px' }}>
                <div className="eyebrow" style={{ fontSize:9 }}>TRUE TO SCALE</div>
                <div className="tnum" style={{ marginTop:6, fontSize:13, fontWeight:600 }}>1 : 1</div>
                <div className="tnum" style={{ marginTop:2, fontSize:10, color:'var(--stone)' }}>placed at 9 cm</div>
              </div>
            </div>
          </Reveal>
        </div>
      </div>
    </section>
  );
}

/* ────────────────────────────── 08 PULL QUOTE */
function PullQuoteV2() {
  return (
    <section style={{ background:'var(--mist)', borderBottom:'1px solid var(--line)' }}>
      <div className="container" style={{ paddingTop:128, paddingBottom:128, textAlign:'center', position:'relative' }}>
        <Reveal>
          <span className="eyebrow" style={{ display:'block', marginBottom:36 }}>WHAT PEOPLE SAY</span>
          <p className="pull-quote" style={{
            fontSize:'clamp(40px, 4.6vw, 72px)',
            maxWidth:1100, margin:'0 auto', color:'var(--ink)',
          }}>
            "Felt like opening an Ordnance Survey map and finding it had been pressed into <em>three&nbsp;dimensions</em>."
          </p>
          <div style={{ marginTop:48, display:'flex', alignItems:'center', justifyContent:'center', gap:18 }}>
            <span style={{ width:36, height:1, background:'var(--ink)' }}/>
            <span className="eyebrow" style={{ fontSize:10 }}>THE GEOGRAPHER QUARTERLY · SPRING 2026</span>
            <span style={{ width:36, height:1, background:'var(--ink)' }}/>
          </div>
        </Reveal>
      </div>
    </section>
  );
}

/* ────────────────────────────── 09 ARCHIVE */
function ArchiveSection({ items, go }) {
  return (
    <section style={{ background:'var(--bone)', borderBottom:'1px solid var(--line)' }}>
      <div className="container" style={{ paddingTop:120, paddingBottom:96 }}>
        <div style={{ display:'flex', justifyContent:'space-between', alignItems:'baseline', marginBottom:48 }}>
          <Reveal>
            <div className="eyebrow" style={{ marginBottom:18 }}>FROM THE STUDIO ARCHIVE</div>
            <h2 className="display-section" style={{ fontSize:'clamp(48px, 5vw, 72px)' }}>Recent commissions.</h2>
          </Reveal>
          <Reveal delay={120}>
            <button className="btn-v2 btn-v2-ghost" onClick={() => go('atlas')}>
              See the archive <span className="btn-arrow">{Icon.arrow}</span>
            </button>
          </Reveal>
        </div>
        <div style={{ display:'grid', gridTemplateColumns:'repeat(4, 1fr)', gap:24 }}>
          {items.map((item, i) => (
            <Reveal key={item.id} delay={i * 80}>
              <ProductCard item={item} onClick={() => go('product', item.id)}/>
            </Reveal>
          ))}
        </div>
      </div>
    </section>
  );
}

/* ────────────────────────────── 10 PRESS STRIP */
function PressStripV2() {
  const quotes = [
    { q:'"You can find your own front door."', a:'Iona Galloway · Architect, Glasgow' },
    { q:'"Mine is a square kilometre of Hampstead Heath. It looks like a fossil."', a:'Dev Patel · London' },
    { q:'"The detail is unreasonable — and exactly right."', a:'Studio Notes · April 2026' },
  ];
  return (
    <section style={{ background:'var(--mist)' }}>
      <div className="container" style={{ paddingTop:96, paddingBottom:96 }}>
        <div className="eyebrow" style={{ textAlign:'center', marginBottom:48 }}>FIELD NOTES FROM READERS</div>
        <div style={{ display:'grid', gridTemplateColumns:'repeat(3, 1fr)', gap:48 }}>
          {quotes.map((p, i) => (
            <Reveal key={i} delay={i * 100}>
              <div style={{ borderTop:'1px solid var(--ink)', paddingTop:22 }}>
                <p style={{ fontSize:22, lineHeight:1.4, fontWeight:500, letterSpacing:'-0.005em' }}>{p.q}</p>
                <div className="eyebrow" style={{ marginTop:22, fontSize:9 }}>{p.a}</div>
              </div>
            </Reveal>
          ))}
        </div>
      </div>
    </section>
  );
}

Object.assign(window, {
  useV2, HomePage,
  HeroV2, HeroModel, HeroPhotoStack, HeroPlan, HeroTriptych,
  MarqueeStripV2, SignatureProcess, ProcessStage,
  FeaturedCollection, TheTwelveGrid,
  CommissionPreviewV2, ARShelfModule, PullQuoteV2, ArchiveSection, PressStripV2,
});
