/* global React */
const { useState, useEffect, useRef, useMemo } = React;

// ====================================================================
// LOCVS — Product Showcase v3
// Drives desire, understanding, waitlist signups.
// ====================================================================

const EZ_HEAVY = 'cubic-bezier(0.16, 1, 0.3, 1)';

// Hero 3D scene loaded from src/hero-scene.jsx → window.HeroScene

// -- Real LOCVS wordmark ---------------------------------------------
// Uses the production SVG brand assets in assets/brand/svg/.
//
// NOTE on brand file naming: files suffixed `-bgoff` have a transparent
// canvas — those are the PRODUCTION wordmarks for placement. Files
// suffixed `-bglight` or `-bgdark` include a solid preview rectangle and
// are reference/exhibit files only; they'll paint a hard color block over
// any surface they sit on. Always prefer the transparent `-bgoff` files.
//
// variant:
//   'dark'       → dark-ink wordmark (cls-1 #666 @ 75%) for LIGHT surfaces
//   'light'      → default cream wordmark w/ chiaroscuro for DARK surfaces (≥20px)
//   'cleanLight' → flat cream wordmark for DARK surfaces at SMALL sizes (11–20px)
//                  — the default-light chiaroscuro collapses into the dark ground
//                    at body-copy scale, so use this variant inline in paragraphs
//   'clean'      → minimal/no-dot dark variant for subtle editorial contexts
const LOCVS_WORDMARK_SRC = {
  dark:       'assets/brand/svg/locvs-wordmark-default-dark-bgoff.svg',
  light:      'assets/brand/svg/locvs-wordmark-default-light-bgoff.svg',
  cleanLight: 'assets/brand/svg/locvs-wordmark-clean-light-bgoff.svg',
  clean:      'assets/brand/svg/locvs-wordmark-clean-dark-bgoff.svg',
};

const LocvsWordmark = ({ height = 18, variant = 'dark', className = '' }) => {
  const src = LOCVS_WORDMARK_SRC[variant] || LOCVS_WORDMARK_SRC.dark;
  const cls = `b-locvs-wm${className ? ' ' + className : ''}`;
  return (
    <img
      src={src}
      alt="LOCVS"
      height={height}
      className={cls}
      style={{
        display: 'inline-block',
        verticalAlign: 'middle',
        height: `${height}px`,
        width: 'auto',
        userSelect: 'none',
        WebkitUserDrag: 'none',
      }}
      draggable={false}
    />
  );
};

// -- LOCVS text wordmark (no dot) -----------------------------------
// Per Gustavo 2026-04-22: the dotted `LocvsWordmark` (the full brand
// signature) is reserved for the top menu bar and the final section
// only. Anywhere else the brand is placed as a mark — section labels,
// diagram stamps, inline editorial wordmarks — uses the text-only
// signature `assets/brand/svg/Locvs-text.svg`: LOCVS set in the brand
// type, no argila dot, ink-only. Sized by `height` prop (aspect 6:1).
const LocvsText = ({ height = 14, className = '' }) => {
  const cls = `b-locvs-text${className ? ' ' + className : ''}`;
  return (
    <img
      src="assets/brand/svg/Locvs-text.svg"
      alt="LOCVS"
      height={height}
      className={cls}
      style={{
        display: 'inline-block',
        verticalAlign: 'baseline',
        height: `${height}px`,
        width: 'auto',
        userSelect: 'none',
        WebkitUserDrag: 'none',
      }}
      draggable={false}
    />
  );
};

// -- LOCVS as tracked typography ------------------------------------
// Per Gustavo 2026-04-21: the wordmark SVG belongs ONLY in the top
// nav, The Model (Framework) section, and the footer. Everywhere else
// LOCVS is set in plain sans typography — capitalised and letter-spaced
// — so the word lives inside the reading rhythm instead of punctuating
// every sentence with a logo. The spacing below reads as "L O C V S"
// but uses CSS letter-spacing rather than literal spaces so copy/paste
// keeps the word intact.
const LocvsWord = ({ className = '' }) => (
  <span className={`b-locvs-word${className ? ' ' + className : ''}`}>LOCVS</span>
);

// -- BARCH wordmark --------------------------------------------------
// Gustavo's studio mark. Used inline wherever the label BARCH would
// otherwise be typed in plain text — founder credit, footer lockup,
// FAQ provenance. Source SVG ships the full "barch" wordmark in a
// single-color build and scales cleanly alongside body copy.
const BarchMark = ({ height = 14, variant = 'dark', className = '' }) => {
  const src = 'assets/brand/barch/barch-wordmark-bgoff-update.svg';
  const cls = `b-barch-mark is-${variant}${className ? ' ' + className : ''}`;
  return (
    <img
      src={src}
      alt="BARCH"
      height={height}
      className={cls}
      style={{
        display: 'inline-block',
        verticalAlign: 'baseline',
        height: `${height}px`,
        width: 'auto',
        userSelect: 'none',
        WebkitUserDrag: 'none',
      }}
      draggable={false}
    />
  );
};

const Arrow = () => (
  <svg className="arr" width="14" height="10" viewBox="0 0 14 10" fill="none" aria-hidden="true">
    <path d="M1 5h12m0 0L9 1m4 4L9 9" stroke="currentColor" strokeWidth="1.5" strokeLinecap="square"/>
  </svg>
);

const Pill = ({ children, dark }) => (
  <span className={`b-pill ${dark ? 'is-dark' : ''}`}>
    <span className="b-pill-dot" aria-hidden="true" />
    {children}
  </span>
);

// -- Scroll progress bar ---------------------------------------------
function ScrollProgress() {
  const ref = useRef(null);
  useEffect(() => {
    const onScroll = () => {
      const h = document.documentElement;
      const max = h.scrollHeight - h.clientHeight;
      const p = max > 0 ? Math.min(1, Math.max(0, h.scrollTop / max)) : 0;
      if (ref.current) ref.current.style.transform = `scaleX(${p})`;
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
    };
  }, []);
  return (
    <div className="b-progress" aria-hidden="true">
      <div ref={ref} className="b-progress-fill" />
    </div>
  );
}

// -- Scroll reveal ----------------------------------------------------
function useReveal() {
  useEffect(() => {
    const els = document.querySelectorAll('.b-rise');
    if (!('IntersectionObserver' in window)) {
      els.forEach(el => el.classList.add('is-in')); return;
    }
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting) {
          e.target.classList.add('is-in');
          io.unobserve(e.target);
        }
      });
    }, { threshold: 0.14, rootMargin: '-5% 0px -5% 0px' });
    els.forEach(el => io.observe(el));
    return () => io.disconnect();
  }, []);
}

// -- 3D tilt on hover ------------------------------------------------
// Writes --highlight-x / --highlight-y custom props so glass ::before
// specular highlights can shift with cursor position (light responds).
function useTilt(max = 4) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let raf = 0;
    const on = (e) => {
      const r = el.getBoundingClientRect();
      const x = (e.clientX - r.left) / r.width;
      const y = (e.clientY - r.top) / r.height;
      const rx = (0.5 - y) * 2 * max;
      const ry = (x - 0.5) * 2 * max;
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => {
        el.style.transition = `transform 100ms ${EZ_HEAVY}`;
        el.style.transform = `perspective(800px) rotateX(${rx}deg) rotateY(${ry}deg) translateY(-4px)`;
        // Light response: follow cursor on the horizontal axis, subtle on Y.
        el.style.setProperty('--highlight-x', `${Math.round(x * 100)}%`);
        el.style.setProperty('--highlight-y', `${Math.round(y * 100)}%`);
      });
    };
    const off = () => {
      cancelAnimationFrame(raf);
      el.style.transition = `transform 400ms ${EZ_HEAVY}`;
      el.style.transform = 'perspective(800px) rotateX(0) rotateY(0) translateY(0)';
      // Return specular to centered rest position.
      el.style.setProperty('--highlight-x', '50%');
      el.style.setProperty('--highlight-y', '0%');
    };
    el.addEventListener('mousemove', on);
    el.addEventListener('mouseleave', off);
    return () => {
      el.removeEventListener('mousemove', on);
      el.removeEventListener('mouseleave', off);
      cancelAnimationFrame(raf);
    };
  }, [max]);
  return ref;
}

// -- Count-up hook ---------------------------------------------------
function useCountUp(targetStr) {
  const [text, setText] = useState('');
  const ref = useRef(null);
  const meta = useMemo(() => {
    const m = String(targetStr).match(/([^\d.]*?)(-?\d+(?:\.\d+)?)([\s\S]*)/);
    if (!m) return { prefix: targetStr, value: 0, suffix: '', decimals: 0 };
    const prefix = m[1] || '';
    const raw = m[2];
    const suffix = m[3] || '';
    const decimals = raw.includes('.') ? 1 : 0;
    return { prefix, value: parseFloat(raw), suffix, decimals };
  }, [targetStr]);
  useEffect(() => {
    setText(`${meta.prefix}0${meta.decimals ? '.0' : ''}${meta.suffix}`);
    const el = ref.current;
    if (!el || typeof IntersectionObserver === 'undefined') {
      setText(String(targetStr));
      return;
    }
    let raf = 0;
    let started = false;
    const easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
    const animate = () => {
      const dur = 1200;
      const start = performance.now();
      const tick = (now) => {
        const t = Math.min(1, (now - start) / dur);
        const v = meta.value * easeOutCubic(t);
        const disp = meta.decimals ? v.toFixed(1) : Math.round(v).toString();
        setText(`${meta.prefix}${disp}${meta.suffix}`);
        if (t < 1) raf = requestAnimationFrame(tick);
      };
      raf = requestAnimationFrame(tick);
    };
    const io = new IntersectionObserver((ents) => {
      ents.forEach(en => {
        if (en.isIntersecting && !started) {
          started = true; animate(); io.disconnect();
        }
      });
    }, { threshold: 0.5 });
    io.observe(el);
    return () => { cancelAnimationFrame(raf); io.disconnect(); };
  }, [meta.prefix, meta.value, meta.suffix, meta.decimals, targetStr]);
  return [ref, text];
}

// -- Nav --------------------------------------------------------------
function Nav() {
  const [scrolled, setScrolled] = useState(false);
  useEffect(() => {
    const on = () => setScrolled(window.scrollY > 80);
    on(); window.addEventListener('scroll', on, { passive: true });
    return () => window.removeEventListener('scroll', on);
  }, []);
  return (
    <nav className={`b-nav ${scrolled ? 'is-scrolled' : ''}`} aria-label="Primary">
      <div className="b-nav-inner">
        <a href="#top" className="b-logo" aria-label="LOCVS home">
          {/* Global scrolled nav sits on Cloud Dancer → dark wordmark */}
          <LocvsWordmark height={20} variant="dark" />
        </a>
        <div className="b-nav-links">
          <a href="#science">The Science</a>
          <a href="#diagram">The Framework</a>
          <a href="#profiles">The Profiles</a>
          <a href="#how">How It Works</a>
          <a href="#faq">FAQ</a>
        </div>
        <a className="b-btn b-btn-primary" href="#waitlist">Join the Waitlist</a>
      </div>
    </nav>
  );
}

// -- Stat cell with count-up + tilt (reused) ------------------------
// Optional `cite` prop appends a footnote superscript → References section.
function StatCell({ n, l, cite }) {
  const [countRef, text] = useCountUp(n);
  const tiltRef = useTilt(4);
  const setRefs = (el) => { countRef.current = el; tiltRef.current = el; };
  return (
    <div className="b-stat" role="listitem" ref={setRefs}>
      <div className="b-stat-n">{text || n}</div>
      <div className="b-stat-l">
        {l}
        {cite && (
          <sup className="b-ref-link">
            <a href={`#ref-${cite}`} aria-label={`See reference ${cite}`}>{cite}</a>
          </sup>
        )}
      </div>
    </div>
  );
}

// -- Section 1: Hero (v2 — video-backed) -----------------------------
// Full-bleed monochrome 3D city video with baked-in V+dot GPS markers.
// Hero is LIGHT because the source video is light; text is dark ink.
// No redundant SVG V-marker overlays — the ones in the video are the
// canonical LOCVS mark and form the visual payoff.
function Hero() {
  const videoRef = useRef(null);
  const heroRef = useRef(null);

  // Slow the video down for cinematic feel.
  useEffect(() => {
    const v = videoRef.current;
    if (!v) return;
    v.playbackRate = 0.75;
    // Safari sometimes pauses autoplay when the tab is backgrounded —
    // nudge it back on when it comes to the front.
    const resume = () => { if (v.paused) v.play().catch(() => {}); };
    document.addEventListener('visibilitychange', resume);
    return () => document.removeEventListener('visibilitychange', resume);
  }, []);

  // Cursor spotlight — update --mx/--my CSS vars in px-percent.
  useEffect(() => {
    const hero = heroRef.current;
    if (!hero) return;
    let raf = 0;
    const onMove = (e) => {
      const r = hero.getBoundingClientRect();
      const mx = ((e.clientX - r.left) / r.width) * 100;
      const my = ((e.clientY - r.top) / r.height) * 100;
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => {
        hero.style.setProperty('--mx', mx.toFixed(2) + '%');
        hero.style.setProperty('--my', my.toFixed(2) + '%');
      });
    };
    hero.addEventListener('mousemove', onMove);
    return () => {
      hero.removeEventListener('mousemove', onMove);
      cancelAnimationFrame(raf);
    };
  }, []);

  return (
    <section id="top" ref={heroRef} className="locvs-hero is-light" aria-label="Hero">
      {/*
        Video background — LIGHT hero (user preference 2026-04-21).
        Treatments baked into frames. We layer a heavy Cloud-Dancer scrim
        on top so the surface reads as cream, the video remains faintly
        visible as a moving substrate, and dark-ink typography is legible.
        Sources: assets/video/ (canonical asset path).
      */}
      <div className="locvs-hero__video-wrap" aria-hidden="true">
        <video
          ref={videoRef}
          className="locvs-hero__video"
          poster="assets/video/locvs_hero_light_poster.jpg"
          autoPlay
          muted
          loop
          playsInline
          preload="metadata"
        >
          <source src="assets/video/locvs_hero_light.webm" type="video/webm" />
          <source src="assets/video/locvs_hero_light.mp4"  type="video/mp4" />
        </video>
      </div>
      {/* Cream wash — layer 1, turns the dark video into a ghost under cream */}
      <div className="locvs-hero__wash" aria-hidden="true" />
      {/* Frosted glass over the top — layer 2, diffuses detail without hiding motion */}
      <div className="locvs-hero__glass" aria-hidden="true" />
      {/* Grain — layer 3, tactile depth */}
      <div className="locvs-hero__grain" aria-hidden="true" />
      {/* Spotlight — cursor interaction, now subtle on light surface */}
      <div className="locvs-hero__spotlight" aria-hidden="true" />

      {/* Nav bar overlaid on the hero */}
      <nav className="locvs-hero__nav" aria-label="Primary">
        <a href="#top" aria-label="LOCVS home">
          {/* Light hero → dark wordmark */}
          <LocvsWordmark height={22} variant="dark" />
        </a>
        <div className="locvs-hero__nav-links">
          <a href="#spatial-intelligence">Spatial Intelligence</a>
          <a href="#problem">Problem</a>
          <a href="#science">Science</a>
          <a href="#experience">Experience</a>
          <a href="#benefits">Benefits</a>
        </div>
        <a className="locvs-cta-mini" href="#waitlist">Join the Waitlist</a>
      </nav>

      {/* Center editorial block */}
      <div className="locvs-hero__content">
        <span className="locvs-hero__eyebrow">
          <span className="locvs-hero__eyebrow-dot" aria-hidden="true" />
          The first Human-Centered Spatial Intelligence
        </span>
        <h1 className="locvs-hero__headline">
          <span className="grad hero-line">Zillow shows properties.</span>
          <span className="grad hero-line">Locus shows <em>yours</em>.</span>
        </h1>
        <p className="locvs-hero__sub">
          A diagnostic that matches buyers to homes by how they actually live.
        </p>
        <div className="locvs-hero__ctas">
          <a className="cta-primary" href="#waitlist">
            Join the Waitlist <span className="arrow"><Arrow/></span>
          </a>
        </div>
      </div>

      {/* Bottom metadata strip: scroll cue (left) · studio coordinates (right).
          Stats strip removed 2026-04-22 — the three citations ($405B / 73% /
          ~60%) now live only in the References section. The hero footer
          returns to a quiet two-element composition: movement cue on the
          left, place signature on the right. */}
      <div className="locvs-hero__footstrip" aria-hidden="false">
        <div className="locvs-hero__scrollcue" aria-hidden="true">
          <span>Scroll</span>
          <svg width="14" height="22" viewBox="0 0 14 22" fill="none" stroke="currentColor" strokeWidth="1.2">
            <rect x="0.5" y="0.5" width="13" height="21" rx="6.5"/>
            <line x1="7" y1="5" x2="7" y2="10"/>
          </svg>
        </div>
        <div className="locvs-hero__coords" aria-label="Studio coordinates">
          25.7617° N <span className="dot">·</span> 80.1918° W
        </div>
      </div>
    </section>
  );
}

// -- Section 2: The Problem (DARK) -----------------------------------
function Problem() {
  const cards = [
    { n: "01", title: "Everyone sees the same listings. Nobody finds the right one.",
      body: "The industry indexes square footage, bedroom count, ZIP code. But two families with identical checklists can need radically different homes. The match is never in the search." },
    { n: "02", title: "Architects design for taste. Taste doesn't explain why a room feels wrong.",
      body: "Pinterest boards capture what someone admires. They miss how someone actually lives. Roughly 60% of residents occupy homes that diverge from their own stated spatial preferences." },
    { n: "03", title: "The industry measures square footage. Nobody measures how a space makes you feel.",
      body: "Listings, proposals, walkthroughs — every one of them built around features. None built around fit. The thing that matters most is the thing no one has instrumentation for." }
  ];
  return (
    <section id="problem" className="b-section is-ink" aria-label="The Problem">
      <div className="b-wrap">
        <div className="b-sechead b-rise">
          <Pill dark>The Problem</Pill>
          <h2 className="b-hed is-gradient">
            People choose homes by what they see. They should choose by how they live.
          </h2>
        </div>
        <div className="b-grid-3">
          {cards.map((c, i) => (
            <article key={i} className={`b-card-dark b-rise d${i+1}`}>
              <span className="b-cardn" aria-hidden="true">{c.n}</span>
              <h3>{c.title}</h3>
              <p>{c.body}</p>
            </article>
          ))}
        </div>
      </div>
    </section>
  );
}

// -- Solution step card with tilt -----------------------------------
function StepCard({ s, d }) {
  const ref = useTilt(4);
  return (
    <article className={`b-card-glass b-rise d${d}`} ref={ref}>
      <span className="b-stepn" aria-hidden="true">{s.n}</span>
      <p className="b-stepl">{s.label}</p>
      <h3>{s.title}</h3>
      <p>{s.body}</p>
    </article>
  );
}

// -- Section 3: Solution + Analogy Box -------------------------------
function Solution() {
  const steps = [
    { n: "01", label: "Diagnose", title: "Three minutes to surface what you can't put into words",
      body: "A visual game uses photo-elicitation and forced-choice trade-offs to capture spatial needs that sit below conscious awareness. Not what you say — what you respond to." },
    { n: "02", label: "Profile", title: "See yourself as one of six spatial archetypes",
      body: "A proprietary algorithm maps you across six dimensions. You get a photoreal rendering of your ideal space — generated for you, not picked from a catalog. Shareable. True." },
    { n: "03", label: "Match", title: "Meet the spaces that actually fit you",
      body: "Your profile matches to tagged properties and design-aligned architects. Not by checklist — by spatial fit. Pre-matched. Ready to show." },
  ];
  return (
    <section id="how" className="b-section is-cloud" aria-label="How It Works">
      <div className="b-wrap">
        <div className="b-sechead b-rise">
          <Pill>How It Works</Pill>
          <h2 className="b-hed">
            A spatial intelligence layer between you and the inventory.
          </h2>
          <p className="b-lead is-wide">
            Think of it as a personality test for how you actually live in a space. Three minutes. Behavioral science. No questionnaires.
          </p>
        </div>
        <div className="b-grid-3">
          {steps.map((s, i) => <StepCard key={s.n} s={s} d={i+1} />)}
        </div>
      </div>
    </section>
  );
}

// -- Section: Familiar (You already know this model) ----------------
// Standalone, LIGHT. Promoted out of Solution 2026-04-21 per brief 3.1.
// Spotify/Netflix/LOCVS analogy carries significant persuasive weight
// as its own beat — burying it inside How-It-Works diluted the signal.
function Familiar() {
  // Abstract SVG marks — one glyph per column. Not logos (legal) but
  // the structural signature of each system: a waveform, a grid, a
  // hexagonal V-dot. Architecturally consistent at 1px strokes, 44×22
  // viewBoxes, argila accent on LOCVS only.
  const Waveform = () => (
    <svg className="b-analogy-mark" viewBox="0 0 44 22" aria-hidden="true" role="img">
      <path d="M1,11 Q3,4 5,11 T9,11 T13,4 T17,11 T21,18 T25,4 T29,11 T33,11 T37,6 T41,11 T43,11"
        fill="none" stroke="#2C2C2E" strokeOpacity="0.78" strokeWidth="1" strokeLinecap="round" />
    </svg>
  );
  const GridMark = () => (
    <svg className="b-analogy-mark" viewBox="0 0 44 22" aria-hidden="true" role="img">
      {[0,1,2,3].map(r => [0,1,2,3,4,5,6,7].map(c => (
        <rect key={`${r}-${c}`} x={2 + c*5.2} y={1 + r*5.2} width="3.6" height="3.6"
          fill="none" stroke="#2C2C2E" strokeOpacity={0.35 + ((r+c) % 3) * 0.22}
          strokeWidth="0.8" />
      )))}
    </svg>
  );
  const HexMark = () => {
    // Hexagon with argila V-dot in the center — the LOCVS signature glyph.
    const cx = 22, cy = 11, r = 8.5;
    const pts = [];
    for (let i = 0; i < 6; i++) {
      const t = -Math.PI / 2 + (Math.PI / 3) * i;
      pts.push([cx + r * Math.cos(t), cy + r * Math.sin(t)]);
    }
    const d = pts.map(([x, y], i) =>
      `${i === 0 ? 'M' : 'L'}${x.toFixed(2)},${y.toFixed(2)}`
    ).join(' ') + ' Z';
    return (
      <svg className="b-analogy-mark" viewBox="0 0 44 22" aria-hidden="true" role="img">
        <path d={d} fill="none" stroke="#B87B5E" strokeOpacity="0.85" strokeWidth="1" />
        {pts.map(([x, y], i) => (
          <circle key={i} cx={x} cy={y} r="0.9" fill="#B87B5E" opacity={i === 0 ? 1 : 0.55} />
        ))}
        <circle cx={cx} cy={cy} r="1.6" fill="#B87B5E" />
      </svg>
    );
  };
  return (
    <section id="familiar" className="b-section is-white" aria-label="You already know this model">
      <div className="b-wrap">
        <div className="b-sechead b-rise">
          <Pill>The Model</Pill>
          <h2 className="b-hed">You already know this model.</h2>
          <p className="b-lead is-wide">
            The best matching systems don't ask what you like. They learn what you respond to — and reveal patterns you couldn't articulate yourself.
          </p>
        </div>
        <div className="b-analogy is-standalone b-rise">
          {/* Connecting rule — 1px hairline threading the three columns,
              with an argila dot at the LOCVS (rightmost) intersection. */}
          <div className="b-analogy-rule" aria-hidden="true">
            <span className="b-analogy-rule-dot" />
          </div>
          <div className="b-analogy-grid">
            <div className="b-analogy-col is-clear">
              <Waveform />
              <h4>Spotify</h4>
              <p>Doesn't ask what genre you like. It learns from what you listen to.</p>
              <em>Discover Weekly — songs you didn't know you'd love.</em>
            </div>
            <div className="b-analogy-col is-clear">
              <GridMark />
              <h4>Netflix</h4>
              <p>Doesn't ask for a movie checklist. It tracks what you watch, when you pause.</p>
              <em>80% of views come from the algorithm.</em>
            </div>
            <div className="b-analogy-col is-self is-frost">
              <HexMark />
              <h4 className="b-analogy-wm"><LocvsText height={18} /></h4>
              <p>Doesn't ask what style you like. It reveals how you experience space.</p>
              <em>A spatial profile that matches you to homes you need.</em>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

// -- Section 4: Science (REWRITE — disciplines, no N numbers) -------
function Science() {
  const disciplines = [
    { n: "01", title: "Environmental Psychology",
      body: "How physical environments shape emotion, behavior, and identity.",
      cite: "Gifford 2014; Evans 2003" },
    { n: "02", title: "Neuroscience applied to Architecture",
      body: "How the brain responds to rooms, light, and proportion — the spatial signals that shape feeling before thought.",
      cite: "Mallgrave 2018; Eberhard 2009" },
    { n: "03", title: "Phenomenology of Space",
      body: "The philosophical tradition of understanding space through lived experience, not measurement.",
      cite: "Bachelard 1958; Pallasmaa 2005" },
    { n: "04", title: "Spatial Cognition",
      body: "How people mentally map, navigate, and form attachments to places.",
      cite: "Tuan 1977; Lynch 1960" },
  ];
  return (
    <section id="science" className="b-section is-white" aria-label="The Science">
      <div className="b-wrap">
        <div className="b-sechead b-rise">
          <Pill>The Science</Pill>
          <h2 className="b-hed is-gradient">
            Built on what we know about how people experience space.
          </h2>
        </div>
        <div className="b-science-split b-rise">
          {/* LEFT — thesis column. Resident-voice italic pullquote + body prose. */}
          <div className="b-science-left">
            <p className="b-science-pull">
              &ldquo;We don&rsquo;t ask what style you like.<br/>
              We reveal how you <em>experience space</em>.&rdquo;
            </p>
            <p className="b-science-body">
              <LocvsWord /> draws on more than 120 peer-reviewed studies across environmental psychology, neuroscience applied to architecture, phenomenology, and spatial cognition. Not a survey. Not a personality test. A diagnostic instrument calibrated to surface what you can&rsquo;t quite articulate about your relationship with space.
            </p>
            <p className="b-science-body">
              Every axis, every item, every score has lineage. A traceable path back to a study, a school of thought, a construction tradition, or a classical commitment to how people live well.
            </p>
          </div>
          {/* RIGHT — four sourced disciplines. Each card anchored by a mono numeral + citation. */}
          <ol className="b-science-disciplines">
            {disciplines.map((s, i) => (
              <li key={i} className={`b-sci-item b-rise d${i+1}`}>
                <span className="b-sci-num">{s.n}</span>
                <div className="b-sci-body">
                  <h3 className="b-sci-title">{s.title}</h3>
                  <p className="b-sci-p">{s.body}</p>
                  <p className="b-sci-cite">{s.cite}</p>
                </div>
              </li>
            ))}
          </ol>
        </div>
      </div>
    </section>
  );
}

// -- Section 5: Two Buyers ------------------------------------------
function TwoBuyers() {
  return (
    <section className="b-section is-cloud" aria-label="Same Building, Two Buyers">
      <div className="b-wrap">
        <div className="b-sechead b-rise" style={{marginBottom: 48}}>
          <Pill>The Difference</Pill>
        </div>
        <div className="b-buyers b-rise">
          <header className="b-buyers-head">
            <h2 className="b-hed-sm">Same building. Same budget. Two buyers.</h2>
            <p className="b-lead">Without <LocvsText height={13} />, both tour the same ten homes. With <LocvsText height={13} />, each sees three — and both find the right one.</p>
          </header>
          <div className="b-buyers-body">
            <div className="b-buyer-col is-pavilion">
              <header className="b-buyer-head">
                <span className="b-bl">Buyer A</span>
                <span className="b-buyer-chip">
                  <span className="b-buyer-dot" aria-hidden="true" style={{ background: "#8A8A8A" }} />
                  <span className="b-buyer-axis">X +6 · Y +6 · Z 0</span>
                </span>
              </header>
              <h3>Precise <em>(Pavilion)</em></h3>
              <p className="b-subhead">Responds to</p>
              <ul>
                <li>Smart systems, floor-to-ceiling glass, clean geometry</li>
                <li>Neutral palette, minimal visible storage</li>
              </ul>
              <p className="b-subhead">Show</p>
              <ul><li>Tech-forward towers, open plans, bay views</li></ul>
              <p className="b-subhead">Avoid</p>
              <ul>
                <li className="is-avoid">Dark wood, Mediterranean finishes</li>
                <li className="is-avoid">Compartmented layouts</li>
              </ul>
            </div>
            <div className="b-buyer-col is-harbor">
              <header className="b-buyer-head">
                <span className="b-bl">Buyer B</span>
                <span className="b-buyer-chip">
                  <span className="b-buyer-dot" aria-hidden="true" style={{ background: "#A0896C" }} />
                  <span className="b-buyer-axis">X +3 · Y −3 · Z −4</span>
                </span>
              </header>
              <h3>Intimate <em>(Harbor)</em></h3>
              <p className="b-subhead">Responds to</p>
              <ul>
                <li>Wood floors, dimmable warm light, enclosed balcony</li>
                <li>Textured walls, bedroom as sanctuary</li>
              </ul>
              <p className="b-subhead">Show</p>
              <ul><li>Boutique low-rise, renovated cottages, wood finishes</li></ul>
              <p className="b-subhead">Avoid</p>
              <ul>
                <li className="is-avoid">Glass towers, open lofts, high floors</li>
                <li className="is-avoid">All-white interiors</li>
              </ul>
            </div>
          </div>
          <div className="b-buyers-foot">
            Same checklist. Two completely different homes. <LocvsText height={13} /> is what tells them apart.
          </div>
        </div>
      </div>
    </section>
  );
}

// -- Section 6: Benefits (tabbed) -----------------------------------
function Benefits() {
  const [tab, setTab] = useState('you');
  const barRef = useRef(null);
  const [indicator, setIndicator] = useState({ left: 0, width: 0, ready: false });
  /* Measure the active tab's position so we can slide a 2px rule under it.
     Uses getBoundingClientRect relative to the tab-bar so it survives
     responsive reflow without extra refs per tab. */
  useEffect(() => {
    const bar = barRef.current;
    if (!bar) return;
    const active = bar.querySelector('.b-tab.is-active');
    if (!active) return;
    const barRect = bar.getBoundingClientRect();
    const rect = active.getBoundingClientRect();
    setIndicator({
      left: rect.left - barRect.left,
      width: rect.width,
      ready: true,
    });
  }, [tab]);
  /* Recompute on resize — otherwise the rule stays pinned to stale pixels
     when the user rotates their device or resizes the viewport. */
  useEffect(() => {
    const onResize = () => {
      const bar = barRef.current;
      if (!bar) return;
      const active = bar.querySelector('.b-tab.is-active');
      if (!active) return;
      const barRect = bar.getBoundingClientRect();
      const rect = active.getBoundingClientRect();
      setIndicator((prev) => ({
        left: rect.left - barRect.left,
        width: rect.width,
        ready: prev.ready,
      }));
    };
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);
  const re = [
    { h: "Match buyers to properties by spatial fit, not just specs",
      p: "Show 3–5 pre-matched properties instead of 15–20. Every showing is calibrated to how the buyer actually lives." },
    { h: "Reduce post-purchase regret — lower renovation spend",
      p: "When the space fits, buyers don't gut-renovate six months later. That's $38K saved. That's a referral earned." },
    { h: "Pre-qualified leads with spatial profiles",
      p: "Each lead arrives with a complete spatial identity. You know what resonates before the door opens." },
    { h: "Differentiate your listings with spatial intelligence",
      p: "Tag your inventory on the same axes buyers are profiled on. Become the brokerage that sees the match." },
  ];
  const arch = [
    { h: "Understand your client's spatial personality before the first sketch",
      p: <>The <LocvsWord /> diagnostic becomes the brief. Start designing from how they actually live — not from a Pinterest board.</> },
    { h: "Design from lived experience, not Pinterest boards",
      p: "Replace the aspirational collage with behavioral data. Pair client axis coordinates to concrete design decisions." },
    { h: "Reduce revision cycles by aligning to spatial identity",
      p: "When the brief is grounded in how the client experiences space, the first proposal lands closer. Fewer cycles, cleaner scope." },
    { h: "Present proposals that speak to how they live, not how they decorate",
      p: "Shared vocabulary from day one. Clients hear themselves in the proposal — not a style they browsed." },
  ];
  const you = [
    { h: "Understand how you actually live in space",
      p: "Three minutes. A portrait of your spatial self — where you feel calm, where you feel dissonant, and why. Not what you pinned. What you live." },
    { h: "Reshape the rooms you belong to",
      p: "Once you can name what a room does to you, you can change it. Small shifts with outsized effects — light, texture, scale, flow — made on evidence instead of taste." },
    { h: "Stop living in spaces that drain you",
      p: <>Most people can't say why a room feels wrong — only that it does. <LocvsWord /> turns that hunch into language, so whatever comes next — moving, renovating, or just rearranging — actually fits who you are.</> },
    { h: "A vocabulary for your own life",
      p: "Your adjective, your synthesis phrase, your six-dimension reading. Yours to keep, share with an architect, or return to whenever space stops making sense." },
  ];
  const items = tab === 're' ? re : tab === 'arch' ? arch : you;
  return (
    <section id="benefits" className="b-section is-white" aria-label="Benefits">
      <div className="b-wrap">
        <div className="b-sechead b-rise">
          <Pill>Benefits</Pill>
          <h2 className="b-hed">Different audience. Same instrument.</h2>
          <div className="b-tabs-bar" role="tablist" style={{marginTop: 12}} ref={barRef}>
            <button role="tab" aria-selected={tab==='you'}
              className={`b-tab ${tab==='you' ? 'is-active' : ''}`} onClick={() => setTab('you')}>
              For You
            </button>
            <button role="tab" aria-selected={tab==='re'}
              className={`b-tab ${tab==='re' ? 'is-active' : ''}`} onClick={() => setTab('re')}>
              Real Estate
            </button>
            <button role="tab" aria-selected={tab==='arch'}
              className={`b-tab ${tab==='arch' ? 'is-active' : ''}`} onClick={() => setTab('arch')}>
              Architects
            </button>
            {/* Sliding 2px rule indicator that tracks the active tab.
                Hidden until first measurement to prevent a 0-width flash. */}
            <span
              className="b-tabs-indicator"
              aria-hidden="true"
              style={{
                transform: `translateX(${indicator.left}px)`,
                width: `${indicator.width}px`,
                opacity: indicator.ready ? 1 : 0,
              }}
            />
          </div>
        </div>
        <div className="b-grid-2 b-tab-panel" key={tab}>
          {items.map((b, i) => (
            <article key={i} className="b-benefit">
              <h3>{b.h}</h3>
              <p>{b.p}</p>
            </article>
          ))}
        </div>
      </div>
    </section>
  );
}

// -- Experience card with tilt --------------------------------------
function ExperienceCard({ s, d }) {
  const ref = useTilt(3.5);
  return (
    <article className={`b-card-glass is-experience b-rise d${d}`} ref={ref}>
      <span className="b-stepn" aria-hidden="true">{s.n}</span>
      <p className="b-stepl">{s.label}</p>
      <h3>{s.title}</h3>
      <p>{s.body}</p>
    </article>
  );
}

// -- Section 7: The Experience (product deliverables) ---------------
function Experience() {
  const outputs = [
    { n: "I", label: "Deliverable", title: "Your Spatial Profile",
      body: "One of six archetypal profiles — Precise, Intimate, Receptive, Versatile, Sublime, Alive — with a first-person synthesis phrase that reads like you wrote it." },
    { n: "II", label: "Deliverable", title: <>The <LocvsText height={11} /> Diagram</>,
      body: "A proprietary hexagonal map of six experiential dimensions: Light, Matter, Scale, Atmosphere, Composition, Meaning." },
    { n: "III", label: "Deliverable", title: "Personalized Narrative",
      body: "An AI-composed text portrait of how you experience space — written about you, for you. Yours to keep." },
    { n: "IV", label: "Deliverable", title: "Spatial Match",
      body: "Properties and architects matched to your profile. Not by checklist — by spatial fit. Ready to meet." },
  ];
  return (
    <section id="experience" className="b-section is-cloud" aria-label="The Experience">
      <div className="b-wrap">
        <div className="b-sechead b-rise">
          <Pill>The Experience</Pill>
          <h2 className="b-hed is-gradient">
            3 minutes. 6 dimensions. A complete spatial portrait.
          </h2>
          <p className="b-lead is-wide">
            What you walk away from <LocvsText height={14} /> with isn't a score. It's a portrait — of how you live, how you want to live, and the spaces that already fit.
          </p>
        </div>
        <div className="b-grid-4">
          {outputs.map((s, i) => <ExperienceCard key={s.n} s={s} d={Math.min(i+1,4)} />)}
        </div>
      </div>
    </section>
  );
}

// -- Section 8 (WhyItWorks) removed 2026-04-21 --------------------
// The three stats ($405B / 73% / ~60%) are already present in the
// hero footstrip with identical sources, and Problem.card02 already
// restates the ~60%. The section was a third repetition and was
// removed in Phase 3. Stats remain visible in the hero and citations
// still anchor into References via #ref-1 / #ref-2 / #ref-3.

// -- Phase 5: The LOCVS Diagram ------------------------------------
// Hexagonal 6D radar — the visual heart of the brand. Six experiential
// dimensions (Light, Matter, Scale, Atmosphere, Composition, Meaning)
// arrayed on a hexagon. A sample profile plot is drawn on top to show
// how an individual reading lands. Canonical per brief DECISION POINT
// 8.5 (Option A). Legacy PT-BR labels were retired 2026-04-21.
//
// Geometry (viewBox 480x480, R=168, cx=cy=240):
//   Vertex i at angle θ = -90° + 60°·i (top = 0, clockwise).
//   Grid rings at 25/50/75/100%. Spokes from center to each vertex.
//   Plot: sample Pavilion-leaning ratios per axis, filled argila @ 0.14.
// Data is static — this is the marketing diagram, not the product diagnostic.
// VitruviusToLocvs — three-state scroll-driven morph with stepper control
// Label system (rebuild 2026-04-21, Directive §C):
//   Every dimension name lives OUTSIDE the geometry now and points in
//   with a hairline leader line — anatomical-chart logic. The only
//   internal label is BELONGING, because BELONGING IS the center; it
//   isn't pointed to, it's where everything converges.
//   All copy is in English; Latin etymologies (firmitas/utilitas/
//   venustas) sit beneath the primary names as the genealogical line.
// ─────────────────────────────────────────────────────────────────────
// One continuous gesture across three legible states:
//   I.   Three concentric rings labeled firmitas · utilitas · venustas
//        — the Vitruvian canon (no human figure; only the circles).
//   II.  The rings separate into a Venn — the convergence.
//   III. The Venn settles with FORMA / FUNCIONALIDADE / ESSÊNCIA at
//        the vertices, SEMIÓTICA / FENOMENOLOGIA / HUMANISMO in the
//        lenses, and BELONGING marked at the triple intersection —
//        the Diagrama LOCVS.
//
// Two drivers cooperate:
//   • Scroll writes --p (0..1) via passive rAF listener.
//   • Stepper snaps to I / II / III by locking --p. Any user scroll
//     releases the lock and hands control back to scroll.
//
// Reduced-motion: snaps to p=1.
function VitruviusToLocvs({ highlight = null, forceStage = false, compact = false }) {
  const ref = React.useRef(null);
  const [locked, setLocked] = React.useState(null); // null or {p, id}
  const [stage, setStage] = React.useState(0);      // 0/1/2 for active stepper pill
  const lockedRef = React.useRef(locked);
  lockedRef.current = locked;

  // Derive stage from current --p to highlight correct stepper pill.
  const readStage = (pVal) => (pVal < 0.33 ? 0 : pVal < 0.78 ? 1 : 2);

  // When the diagram is embedded in the tabs studio it stays pinned at
  // state III (labels fully resolved). Tab selection drives the region
  // highlight instead of scroll.
  React.useEffect(() => {
    const node = ref.current;
    if (!node || !forceStage) return;
    node.style.setProperty('--p', '1');
    setStage(2);
  }, [forceStage]);

  React.useEffect(() => {
    if (forceStage) return; // studio mode owns --p
    const node = ref.current;
    if (!node) return;
    if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
      node.style.setProperty('--p', '1');
      setStage(2);
      return;
    }
    let ticking = false;
    const update = () => {
      ticking = false;
      if (lockedRef.current !== null) return; // stepper owns --p
      const rect = node.getBoundingClientRect();
      const winH = window.innerHeight || document.documentElement.clientHeight;
      // Morph window: enter at ~85%vh, complete by ~15%vh. Because the
      // figure is sticky inside the stage, the real scroll-to-p mapping
      // is driven by how far the stage has scrolled under the sticky
      // region — we use the figure's own getBoundingClientRect.top.
      const raw = Math.max(0, Math.min(1, (winH * 0.85 - rect.top) / (winH * 0.70)));
      const p = raw * raw * (3 - 2 * raw); // smoothstep
      node.style.setProperty('--p', p.toFixed(3));
      setStage((s) => {
        const next = readStage(p);
        return next === s ? s : next;
      });
    };
    const onScroll = () => {
      if (ticking) return;
      ticking = true;
      window.requestAnimationFrame(update);
    };
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    update();
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
    };
  }, [forceStage]);

  // Apply locked --p whenever it changes, and install a one-shot
  // unlock listener on user scroll.
  React.useEffect(() => {
    const node = ref.current;
    if (!node || locked === null) return;
    node.style.setProperty('--p', locked.p.toFixed(3));
    setStage(readStage(locked.p));
    const release = () => setLocked(null);
    // Delay unlock install so the scroll event that caused setLocked
    // doesn't immediately unlock us.
    const t = setTimeout(() => {
      window.addEventListener('wheel', release, { passive: true, once: true });
      window.addEventListener('touchstart', release, { passive: true, once: true });
      window.addEventListener('keydown', release, { once: true });
    }, 60);
    return () => {
      clearTimeout(t);
      window.removeEventListener('wheel', release);
      window.removeEventListener('touchstart', release);
      window.removeEventListener('keydown', release);
    };
  }, [locked]);

  const snap = (p) => setLocked({ p, id: Date.now() });

  // Geometry — viewBox 600×480. Center at (300, 240). Rings at
  // [40, 78, 120]. Venn circles r=120, separated by translation.
  const CX = 300, CY = 240;
  const ringR = [40, 78, 120];

  const figureCls = [
    'b-vitruvius',
    forceStage ? 'is-studio' : '',
    compact ? 'is-compact' : '',
    highlight ? `is-hl-${highlight}` : '',
  ].filter(Boolean).join(' ');

  return (
    <figure
      className={figureCls}
      ref={ref}
      data-highlight={highlight || undefined}
      aria-label="Vitruvian concentric circles transforming into the LOCVS Diagram">

      <svg
        viewBox="-130 -70 860 620"
        xmlns="http://www.w3.org/2000/svg"
        role="img"
        aria-hidden="true"
      >
        <defs>
          <radialGradient id="vitloc-halo" cx="50%" cy="50%" r="50%">
            <stop offset="0%" stopColor="rgba(184,123,94,0.16)" />
            <stop offset="100%" stopColor="rgba(184,123,94,0)" />
          </radialGradient>
          <radialGradient id="vit-venn-forma" cx="50%" cy="50%" r="55%">
            <stop offset="0%" stopColor="rgba(138,138,138,0.14)" />
            <stop offset="100%" stopColor="rgba(138,138,138,0.03)" />
          </radialGradient>
          <radialGradient id="vit-venn-func" cx="50%" cy="50%" r="55%">
            <stop offset="0%" stopColor="rgba(125,154,107,0.14)" />
            <stop offset="100%" stopColor="rgba(125,154,107,0.03)" />
          </radialGradient>
          <radialGradient id="vit-venn-ess" cx="50%" cy="50%" r="55%">
            <stop offset="0%" stopColor="rgba(184,123,94,0.14)" />
            <stop offset="100%" stopColor="rgba(184,123,94,0.03)" />
          </radialGradient>
          <radialGradient id="vit-venn-glow" cx="50%" cy="50%" r="50%">
            <stop offset="0%" stopColor="rgba(184,123,94,0.38)" />
            <stop offset="55%" stopColor="rgba(184,123,94,0.11)" />
            <stop offset="100%" stopColor="rgba(184,123,94,0)" />
          </radialGradient>
        </defs>

        {/* Argila halo behind the eventual LOCVS center — blooms late. */}
        <circle className="b-vit-halo" cx={CX} cy={CY + 8} r="160" fill="url(#vitloc-halo)" />

        {/* Structural crosshair removed 2026-04-22. The halo is now the only
            anchor at the center; the BELONGING wordmark sits on the geometric
            centroid of the three circles, so a marker is redundant. */}

        {/* === STATE I — VITRUVIAN CONCENTRIC RINGS + TRIAD CAPTION === */}
        <g className="b-vit-rings">
          {ringR.map((r, i) => (
            <circle key={`ring-${i}`} className="b-vit-ring"
              cx={CX} cy={CY} r={r}
              fill="none"
              stroke="#2C2C2E"
              strokeOpacity={0.22 + i * 0.08}
              strokeWidth="0.95" />
          ))}
          {/* Triad caption — Vitruvius's three categories as a unified line,
              anchored below the outermost ring. Italic ancestors only; the
              contemporary names arrive in State III. */}
          <text className="b-vit-ring-cap" x={CX} y="392" textAnchor="middle"
            fontFamily="Cormorant Garamond, serif" fontSize="15"
            fontStyle="italic" fill="#555558">
            <tspan>firmitas</tspan>
            <tspan dx="10" fill="#B0B0B6">·</tspan>
            <tspan dx="10">utilitas</tspan>
            <tspan dx="10" fill="#B0B0B6">·</tspan>
            <tspan dx="10">venustas</tspan>
          </text>
          <text className="b-vit-ring-sub" x={CX} y="412" textAnchor="middle"
            fontFamily="Inter, system-ui, sans-serif" fontSize="9.5"
            fontWeight="500" letterSpacing="2.8" fill="#84848A">
            VITRUVIUS · c. 30 BCE
          </text>
        </g>

        {/* === STATE II — VENN CIRCLES — fade in 0.30 → 0.55, separate 0.35 → 0.75 */}
        <g className="b-vit-venn">
          <g className="b-vit-venn-c is-top is-forma" data-region="forma">
            <circle cx={CX} cy={CY} r="120"
              fill="url(#vit-venn-forma)"
              stroke="rgba(44,44,46,0.42)" strokeWidth="1" />
          </g>
          <g className="b-vit-venn-c is-bl is-func" data-region="func">
            <circle cx={CX} cy={CY} r="120"
              fill="url(#vit-venn-func)"
              stroke="rgba(125,154,107,0.50)" strokeWidth="1" />
          </g>
          <g className="b-vit-venn-c is-br is-ess" data-region="ess">
            <circle cx={CX} cy={CY} r="120"
              fill="url(#vit-venn-ess)"
              stroke="rgba(184,123,94,0.50)" strokeWidth="1" />
          </g>
        </g>

        {/* === STATE III — BELONGING centerpiece (fades in 0.65 → 0.90) ===
            Cross marker removed 2026-04-22 — the warm argila glow alone is
            now the visual anchor. The BELONGING wordmark (rendered below
            inside .b-vit-labels) sits on the geometric centroid of the
            three circles, which is the true center of the convergence. */}
        <g className="b-vit-center is-belong" data-region="belong">
          <circle cx={CX} cy={CY + 8} r="44" fill="url(#vit-venn-glow)" />
        </g>

        {/* === STATE III — EXTERNAL CALLOUT LABELS (fade in 0.78 → 1) ===
            Anatomical-chart logic. Every dimension name sits outside the
            geometry and points in with a hairline leader line that
            terminates with a small open dot at the target region.
            BELONGING is the only internal label because BELONGING is
            the center — it isn't pointed to, it IS the convergence.

            Region target points (state III, sep=1):
              FORM      vertex →  (300,  90)  top of top circle
              FUNCTION  vertex →  (165, 350)  bottom-left unique region
              ESSENCE   vertex →  (435, 350)  bottom-right unique region
              SEMIOTICS lens   →  (240, 215)  top∩bl intersection center
              PHENOMENOLOGY    →  (360, 215)  top∩br intersection center
              HUMANISM  lens   →  (300, 360)  bl∩br intersection center
              BELONGING center →  (300, 248)  triple intersection

            Stroke conventions:
              Leader line   stroke #2C2C2E @ 0.32 opacity, 0.6 width
              Terminal mark open circle r=2.2, stroke matches leader
        */}
        <g className="b-vit-labels">

          {/* ============ FORM — top center, above geometry ============ */}
          <g className="b-vit-callout is-forma" data-region="forma">
            {/* Leader: short vertical hairline + terminal dot */}
            <line className="b-vit-leader" x1="300" y1="-2" x2="300" y2="78"
              stroke="#2C2C2E" strokeOpacity="0.32" strokeWidth="0.6" />
            <circle className="b-vit-tick" cx="300" cy="80" r="2.2"
              fill="none" stroke="#2C2C2E" strokeOpacity="0.42" strokeWidth="0.7" />
            {/* Label stack — name above, Latin etymology below */}
            <text x="300" y="-30" textAnchor="middle"
              fontFamily="Inter, system-ui, sans-serif" fontSize="12"
              fontWeight="600" letterSpacing="2.6" fill="#2C2C2E">
              FORM
            </text>
            <text x="300" y="-14" textAnchor="middle"
              fontFamily="Cormorant Garamond, serif" fontSize="13"
              fontStyle="italic" fill="#84848A">
              firmitas
            </text>
          </g>

          {/* ============ FUNCTION — bottom-left vertex callout ============ */}
          <g className="b-vit-callout is-func" data-region="func">
            {/* L-shaped leader: out to left margin, then up to a dot at the region */}
            <path className="b-vit-leader"
              d="M 165,350 L 110,350 L -8,440"
              fill="none" stroke="#2C2C2E" strokeOpacity="0.32" strokeWidth="0.6" />
            <circle className="b-vit-tick" cx="165" cy="350" r="2.2"
              fill="none" stroke="#2C2C2E" strokeOpacity="0.42" strokeWidth="0.7" />
            {/* Label — right-anchored sitting in left margin */}
            <text x="-12" y="446" textAnchor="end"
              fontFamily="Inter, system-ui, sans-serif" fontSize="12"
              fontWeight="600" letterSpacing="2.6" fill="#2C2C2E">
              FUNCTION
            </text>
            <text x="-12" y="462" textAnchor="end"
              fontFamily="Cormorant Garamond, serif" fontSize="13"
              fontStyle="italic" fill="#84848A">
              utilitas
            </text>
          </g>

          {/* ============ ESSENCE — bottom-right vertex callout ============ */}
          <g className="b-vit-callout is-ess" data-region="ess">
            <path className="b-vit-leader"
              d="M 435,350 L 490,350 L 608,440"
              fill="none" stroke="#2C2C2E" strokeOpacity="0.32" strokeWidth="0.6" />
            <circle className="b-vit-tick" cx="435" cy="350" r="2.2"
              fill="none" stroke="#2C2C2E" strokeOpacity="0.42" strokeWidth="0.7" />
            <text x="612" y="446" textAnchor="start"
              fontFamily="Inter, system-ui, sans-serif" fontSize="12"
              fontWeight="600" letterSpacing="2.6" fill="#2C2C2E">
              ESSENCE
            </text>
            <text x="612" y="462" textAnchor="start"
              fontFamily="Cormorant Garamond, serif" fontSize="13"
              fontStyle="italic" fill="#84848A">
              venustas
            </text>
          </g>

          {/* ============ SEMIOTICS — top-left intersection callout ============ */}
          <g className="b-vit-callout is-semio" data-region="semio">
            {/* L-shaped leader: from lens center, out to top-left corner */}
            <path className="b-vit-leader"
              d="M 240,215 L 110,215 L -8,90"
              fill="none" stroke="#2C2C2E" strokeOpacity="0.32" strokeWidth="0.6" />
            <circle className="b-vit-tick" cx="240" cy="215" r="2.2"
              fill="none" stroke="#2C2C2E" strokeOpacity="0.42" strokeWidth="0.7" />
            <text x="-12" y="78" textAnchor="end"
              fontFamily="Inter, system-ui, sans-serif" fontSize="11"
              fontWeight="600" letterSpacing="2.4" fill="#2C2C2E">
              SEMIOTICS
            </text>
            <text x="-12" y="94" textAnchor="end"
              fontFamily="Inter, system-ui, sans-serif" fontSize="9"
              fontWeight="500" letterSpacing="1.6" fill="#84848A">
              FORM ∩ FUNCTION
            </text>
          </g>

          {/* ============ PHENOMENOLOGY — top-right intersection callout ============ */}
          <g className="b-vit-callout is-pheno" data-region="pheno">
            <path className="b-vit-leader"
              d="M 360,215 L 490,215 L 608,90"
              fill="none" stroke="#2C2C2E" strokeOpacity="0.32" strokeWidth="0.6" />
            <circle className="b-vit-tick" cx="360" cy="215" r="2.2"
              fill="none" stroke="#2C2C2E" strokeOpacity="0.42" strokeWidth="0.7" />
            <text x="612" y="78" textAnchor="start"
              fontFamily="Inter, system-ui, sans-serif" fontSize="11"
              fontWeight="600" letterSpacing="2.4" fill="#2C2C2E">
              PHENOMENOLOGY
            </text>
            <text x="612" y="94" textAnchor="start"
              fontFamily="Inter, system-ui, sans-serif" fontSize="9"
              fontWeight="500" letterSpacing="1.6" fill="#84848A">
              FORM ∩ ESSENCE
            </text>
          </g>

          {/* ============ HUMANISM — bottom intersection, below geometry ============ */}
          <g className="b-vit-callout is-human" data-region="human">
            {/* Leader: short vertical from below center up to the bottom lens */}
            <line className="b-vit-leader" x1="300" y1="362" x2="300" y2="468"
              stroke="#2C2C2E" strokeOpacity="0.32" strokeWidth="0.6" />
            <circle className="b-vit-tick" cx="300" cy="360" r="2.2"
              fill="none" stroke="#2C2C2E" strokeOpacity="0.42" strokeWidth="0.7" />
            <text x="300" y="486" textAnchor="middle"
              fontFamily="Inter, system-ui, sans-serif" fontSize="11"
              fontWeight="600" letterSpacing="2.4" fill="#2C2C2E">
              HUMANISM
            </text>
            <text x="300" y="502" textAnchor="middle"
              fontFamily="Inter, system-ui, sans-serif" fontSize="9"
              fontWeight="500" letterSpacing="1.6" fill="#84848A">
              FUNCTION ∩ ESSENCE
            </text>
          </g>

          {/* ============ BELONGING — internal, anchored to the convergence ============
              Repositioned 2026-04-22 to the true geometric centroid of
              the three circle centers at sep=1. Centers sit at
              (300,170), (230,285), (370,285); centroid is (300, 246.67).
              Wordmark is now centered on that point with dominant-baseline
              "middle" so it never crosses a circle stroke. The cross
              marker has been removed (see .b-vit-center above) — the
              halo glow alone is the visual anchor. */}
          <g className="b-vit-label-center is-belong" data-region="belong">
            <text x="300" y="244" textAnchor="middle" dominantBaseline="middle"
              fontFamily="Inter, system-ui, sans-serif" fontSize="10.5"
              fontWeight="600" letterSpacing="2.6" fill="#2C2C2E">
              BELONGING
            </text>
            <text x="300" y="262" textAnchor="middle" dominantBaseline="middle"
              fontFamily="Cormorant Garamond, serif" fontSize="11"
              fontStyle="italic" fill="#84848A">
              where the three converge
            </text>
          </g>
        </g>
      </svg>

      {/* Stepper — three pills that snap to I / II / III. Active pill tracks
          the current morph state; user scroll releases any lock and hands
          control back to scroll. Hidden when the diagram is embedded in the
          Framework studio (tabs own region navigation there). */}
      {!forceStage && <div className="b-vit-stepper" role="tablist" aria-label="Diagram evolution">
        {[
          { n: 'I',   label: 'Vitruvius',   p: 0 },
          { n: 'II',  label: 'Convergence', p: 0.5 },
          { n: 'III', label: 'LOCVS',       p: 1 },
        ].map((s, i) => (
          <button
            key={s.n}
            type="button"
            role="tab"
            aria-selected={stage === i}
            className={`b-vit-step${stage === i ? ' is-active' : ''}`}
            onClick={() => snap(s.p)}
          >
            <span className="b-vit-step-n">{s.n}</span>
            <span className="b-vit-step-label">{s.label}</span>
          </button>
        ))}
      </div>}

      {/* Credit figcaption removed 2026-04-22 — the Framework section's
          Lineage bar (Vitruvius → Alberti → Semper → Frampton → 2026)
          now sits above the studio and provides richer genealogy context.
          Keeping a second mini-credit below the diagram read as noise. */}
    </figure>
  );
}

// ─────────────────────────────────────────────────────────────────────
// Lineage — interactive theoretical genealogy
// ─────────────────────────────────────────────────────────────────────
// Five eras on a continuous thread: Vitruvius → Alberti → Semper →
// Frampton → 2026 / LOCVS. Each era is a clickable node on the thread;
// the active era expands a detail panel beneath the timeline showing
// what that thinker contributed to the framework. Default selection is
// the present moment (LOCVS) — the inheritance arrives at the reader.
// Rewritten 2026-04-22 (Directive §D): replaces the static dl-style
// lineage strip with an interactive horizontal timeline that surfaces
// the conceptual evolution rather than just listing names.
function Lineage() {
  const eras = [
    {
      id: 'vit', n: 'I', year: '1st c. BCE', name: 'Vitruvius',
      term: 'firmitas · utilitas · venustas',
      claim: 'The triad — the original three categories.',
      gives: 'The bones of the diagram. Three categories that have survived two millennia of architectural thought, still standing as the most economical description of what architecture is asked to be.'
    },
    {
      id: 'alb', n: 'II', year: '15th c.', name: 'Alberti',
      term: 'pulchritudo vs ornamentum',
      claim: 'Beauty as integral, not applied.',
      gives: 'The first move toward ESSENCE: beauty is the inner concord of the parts, not decoration laid over form. Architecture begins to be read as a system whose meaning is structural, not stylistic.'
    },
    {
      id: 'sem', n: 'III', year: '19th c.', name: 'Semper',
      term: 'four elements · Bekleidung',
      claim: 'Material culture as the source of architectural meaning.',
      gives: 'Hearth, mound, roof, enclosure — the cultural origins of architectural elements. The earliest rigorous account of how matter itself carries meaning, and the seed of what later becomes phenomenology.'
    },
    {
      id: 'fra', n: 'IV', year: '20th c.', name: 'Frampton',
      term: 'tectonic culture',
      claim: 'How construction itself becomes expressive.',
      gives: 'The poetics of construction. PHENOMENOLOGY meets craft: how a joint, a beam, a section drawing carry the full weight of architectural intent. Resistance to the pure-image culture of late modernism.'
    },
    {
      id: 'now', n: 'V', year: '2026', name: 'LOCVS',
      term: 'contemporary reconstruction',
      claim: 'Two millennia, honestly updated.',
      gives: 'The Vitruvian triad recompiled with phenomenology, environmental psychology, and neuroscience applied to architecture — and turned into an instrument that can read a person\u2019s spatial signature on three measurable axes.'
    },
  ];
  const [active, setActive] = useState('now');
  const listRef = useRef(null);
  const order = eras.map((e) => e.id);

  const onKey = (e) => {
    const idx = order.indexOf(active);
    if (idx < 0) return;
    let next = null;
    if (e.key === 'ArrowRight' || e.key === 'ArrowDown') next = order[(idx + 1) % order.length];
    else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') next = order[(idx - 1 + order.length) % order.length];
    else if (e.key === 'Home') next = order[0];
    else if (e.key === 'End') next = order[order.length - 1];
    if (next) {
      e.preventDefault();
      setActive(next);
      const btn = listRef.current && listRef.current.querySelector(`[data-era="${next}"]`);
      if (btn) btn.focus();
    }
  };

  const activeEra = eras.find((e) => e.id === active) || eras[eras.length - 1];
  const activeIdx = order.indexOf(active);
  // Thread fill — the inheritance "arrives" at the active node.
  const fillPct = order.length > 1 ? (activeIdx / (order.length - 1)) * 100 : 100;

  return (
    <div className="b-genealogy b-rise" aria-label="Theoretical lineage">
      <div className="b-genealogy-head">
        <span className="b-genealogy-label">The lineage</span>
        <span className="b-genealogy-sublabel">Two millennia of compounding inheritance — click an era</span>
      </div>

      <ol
        className="b-genealogy-thread"
        role="tablist"
        aria-label="Era timeline"
        ref={listRef}
        onKeyDown={onKey}
        style={{ '--gen-fill': `${fillPct}%` }}
      >
        {eras.map((era, i) => {
          const isActive = era.id === active;
          const isNow = era.id === 'now';
          const isReached = i <= activeIdx;
          return (
            <li
              key={era.id}
              className={[
                'b-gen-node',
                isActive ? 'is-active' : '',
                isNow ? 'is-now' : '',
                isReached ? 'is-reached' : '',
              ].filter(Boolean).join(' ')}
            >
              <button
                type="button"
                role="tab"
                aria-selected={isActive}
                aria-controls="gen-detail"
                tabIndex={isActive ? 0 : -1}
                data-era={era.id}
                className="b-gen-node-btn"
                onClick={() => setActive(era.id)}
              >
                <span className="b-gen-marker" aria-hidden="true">
                  <span className="b-gen-marker-n">{era.n}</span>
                </span>
                <span className="b-gen-stamp">
                  <span className="b-gen-year">{era.year}</span>
                  <span className="b-gen-name">
                    {isNow ? <LocvsText height={11} /> : era.name}
                  </span>
                </span>
              </button>
            </li>
          );
        })}
      </ol>

      <div
        id="gen-detail"
        className={`b-gen-detail${activeEra.id === 'now' ? ' is-now' : ''}`}
        role="tabpanel"
        aria-labelledby={`gen-tab-${activeEra.id}`}
        key={activeEra.id}
      >
        <div className="b-gen-detail-meta">
          <span className="b-gen-detail-n">{activeEra.n}</span>
          <span className="b-gen-detail-year">{activeEra.year}</span>
          <span className="b-gen-detail-name">
            {activeEra.id === 'now' ? (
              <LocvsText height={12} />
            ) : activeEra.name}
          </span>
        </div>
        <p className="b-gen-detail-term"><em>{activeEra.term}</em></p>
        <p className="b-gen-detail-claim">{activeEra.claim}</p>
        <p className="b-gen-detail-body">{activeEra.gives}</p>
      </div>
    </div>
  );
}

function DiagramLOCVS() {
  // The seven positions of the Diagrama LOCVS:
  //   three primary vertices (Vitruvian ancestors reconstructed),
  //   three pairwise intersections (disciplines that bridge them),
  //   one centerpoint (BELONGING — where all three converge).
  // Rewritten 2026-04-21 (Directive §B): consolidated tab studio. The
  // diagram sticks to viewport-center while tabs expose each position.
  const dims = [
    { tier: "PRIMARY",   key: "forma",   n: "I",   label: "FORM",
      parent: "firmitas", role: "the body of architecture",
      d: "Structure, material, weight, geometry — what makes a place stand, and the substance out of which it stands." },
    { tier: "PRIMARY",   key: "func",    n: "II",  label: "FUNCTION",
      parent: "utilitas", role: "the life of architecture",
      d: "Program, flow, use — how a space receives the body, organizes movement, and serves the ritual of daily living." },
    { tier: "PRIMARY",   key: "ess",     n: "III", label: "ESSENCE",
      parent: "venustas", role: "the soul of architecture",
      d: "Meaning, beauty, resonance — what a place becomes once it is lived in. The quality that makes one room matter more than another." },
    { tier: "INTERSECTION", key: "semio", n: "IV",  label: "SEMIOTICS",
      parent: "FORM ∩ FUNCTION", role: "the language of built form",
      d: "How structure becomes sign. The arch that announces an entrance, the threshold that names a change of register, the ceiling that measures importance." },
    { tier: "INTERSECTION", key: "pheno", n: "V",   label: "PHENOMENOLOGY",
      parent: "FORM ∩ ESSENCE", role: "the felt presence of matter",
      d: "How stone, light, and silence register in the body before the mind names them. The temperature of a wall, the acoustic of a room, the grain of a floor underfoot." },
    { tier: "INTERSECTION", key: "human", n: "VI",  label: "HUMANISM",
      parent: "FUNCTION ∩ ESSENCE", role: "the care of dwelling",
      d: "How use becomes ritual. How a kitchen becomes hearth, a bedroom becomes shelter, a hallway becomes the space where the day is laid down." },
    { tier: "CENTER",    key: "belong", n: "VII", label: "BELONGING",
      parent: "where the three converge", role: "the singular center",
      d: "Not a style. Not a taste. The architectural signature of how a person belongs in space — the single point where form, function, and essence meet in a reading that is entirely yours." },
  ];
  const [active, setActive] = useState("forma");
  const listRef = useRef(null);

  // Keyboard navigation across the vertical tablist (↑↓ cycle, Home/End jump).
  const order = dims.map((d) => d.key);
  const onKey = (e) => {
    const idx = order.indexOf(active);
    if (idx < 0) return;
    let next = null;
    if (e.key === "ArrowDown" || e.key === "ArrowRight") next = order[(idx + 1) % order.length];
    else if (e.key === "ArrowUp" || e.key === "ArrowLeft") next = order[(idx - 1 + order.length) % order.length];
    else if (e.key === "Home") next = order[0];
    else if (e.key === "End") next = order[order.length - 1];
    if (next) {
      e.preventDefault();
      setActive(next);
      const btn = listRef.current && listRef.current.querySelector(`[data-key="${next}"]`);
      if (btn) btn.focus();
    }
  };

  const activeDim = dims.find((d) => d.key === active) || dims[0];
  const tierOf = (k) => ({
    PRIMARY: "Primary triad",
    INTERSECTION: "Intersections",
    CENTER: "Center",
  })[k];

  return (
    <section id="diagram" className="b-section is-cloud b-diagram b-fw" aria-label="The LOCVS Diagram">
      <div className="b-wrap">
        <div className="b-sechead b-rise">
          <Pill>The Framework</Pill>
          <h2 className="b-hed">
            Two millennia of architectural theory, honestly updated.
          </h2>
          <p className="b-lead is-wide">
            The <LocvsText height={14} /> Diagram is the Vitruvian triad — <em>firmitas</em>, <em>utilitas</em>, <em>venustas</em> — reconstructed with phenomenology, environmental psychology, and neuroscience applied to architecture. Three primary dimensions. Three pairwise intersections. One center: <strong>BELONGING</strong>.
          </p>
        </div>

        {/* The Lineage — interactive timeline (2026-04-22, Directive §D).
            Five eras on a continuous thread; each era is a clickable
            node that expands a detail panel showing what that thinker
            contributed to the framework. Default selection is "now"
            (LOCVS) so the inheritance arrives at the reader. See the
            <Lineage /> component above for full data + behaviour. */}
        <Lineage />

        {/* The Studio — a single elevated glass block that consolidates
            every positional reading of the diagram into one consistent
            view. Left column: the diagram on top + the reading panel
            below (aligned, same column). Right column: the vertical
            tablist with every position of the diagram — primary
            vertices, pairwise intersections, and the center.
            Rewritten 2026-04-22 (Directive §C): panel moved out of the
            sidebar into the left column so reading sits directly under
            the figure it explains; sticky behaviour removed so the
            grid flows predictably without the diagram's highlight
            transforms causing layout jitter. */}
        {/* Directive v2 §4.1 — outer glass wrapper creates the double-
            layer "museum display case" effect. The softer .b-diagram-
            glass (0.58 frost) holds the denser .b-fw-stage (~0.76) so
            the diagram reads as glass-inside-glass. */}
        <div className="b-diagram-glass b-rise">
          <div className="b-fw-stage glass-frost glass-highlight">
          <div className="b-fw-figure">
            <div className="b-fw-figure-frame">
              <VitruviusToLocvs highlight={active} forceStage compact />
              {/* Figure caption strip removed 2026-04-22. The panel
                  below repeats the active dimension identity as its
                  heading; keeping a caption pill here duplicated the
                  same information twice within ~80 pixels of each
                  other. The active tab in the right rail is the third
                  redundant surface — we now rely on the panel
                  heading as the single live identity of the selection. */}
            </div>

            <div
              id="fw-panel"
              className={`b-fw-panel is-${activeDim.tier.toLowerCase()}`}
              role="tabpanel"
              aria-labelledby={`fw-tab-${activeDim.key}`}
              key={activeDim.key}
            >
              <header className="b-fw-panel-head">
                <span className="b-fw-panel-n">{activeDim.n}</span>
                <div className="b-fw-panel-ident">
                  <h3 className="b-fw-panel-label">{activeDim.label}</h3>
                  <p className="b-fw-panel-parent">
                    {activeDim.tier === "PRIMARY" ? <em>{activeDim.parent}</em> : activeDim.parent}
                  </p>
                </div>
              </header>
              <p className="b-fw-panel-role">{activeDim.role}</p>
              <p className="b-fw-panel-body">{activeDim.d}</p>
            </div>
          </div>

          <div className="b-fw-sidebar">
            <div
              className="b-fw-tabs"
              role="tablist"
              aria-orientation="vertical"
              aria-label="Positions of the LOCVS Diagram"
              ref={listRef}
              onKeyDown={onKey}
            >
              {["PRIMARY", "INTERSECTION", "CENTER"].map((tier) => (
                <div key={tier} className={`b-fw-group is-${tier.toLowerCase()}`}>
                  <span className="b-fw-group-label">{tierOf(tier)}</span>
                  <ul className="b-fw-group-list">
                    {dims.filter((d) => d.tier === tier).map((d) => {
                      const isActive = d.key === active;
                      return (
                        <li key={d.key}>
                          <button
                            type="button"
                            role="tab"
                            id={`fw-tab-${d.key}`}
                            aria-selected={isActive}
                            aria-controls="fw-panel"
                            tabIndex={isActive ? 0 : -1}
                            data-key={d.key}
                            className={`b-fw-tab is-${tier.toLowerCase()}${isActive ? " is-active" : ""}`}
                            onClick={() => setActive(d.key)}
                          >
                            <span className="b-fw-tab-n">{d.n}</span>
                            <span className="b-fw-tab-body">
                              <span className="b-fw-tab-label">{d.label}</span>
                              <span className="b-fw-tab-parent">
                                {d.tier === "PRIMARY" ? <em>{d.parent}</em> : d.parent}
                              </span>
                            </span>
                            {/* Chevron replaced 2026-04-22 by a left-edge
                                color bar (see .b-fw-tab::before in CSS).
                                Seven rotating chevrons created visual
                                noise with no navigational utility — a
                                single 2px tier-coloured rail on the
                                left of each active tab does the same
                                job with less ink. */}
                          </button>
                        </li>
                      );
                    })}
                  </ul>
                </div>
              ))}
            </div>
          </div>
        </div>
        {/* close .b-diagram-glass wrapper (Directive v2 §4.1) */}
        </div>
      </div>
    </section>
  );
}

// -- Phase 6: The Six Profiles gallery -----------------------------
// The six archetypal profiles. Each profile is a compass heading —
// not a box. Every person reads primarily as one and secondarily as
// another. This gallery shows the full map so the reader can imagine
// where they might land.
//
// Colors come from CLAUDE.md / config/coty.ts (post-diagnostic palette).
// Adjective = protagonist (UI label). Noun in parens = internal key.
function SixProfiles() {
  // Coordinates intentionally omitted from the marketing surface —
  // the XYZ formula is the LOCVS trade secret (see CLAUDE.md
  // "Never Alter" list). Public cards show adjective · noun · phrase
  // only; the real coordinates live in lib/data/profiles.ts.
  const profiles = [
    { adj: "Precise",   noun: "Pavilion", color: "#8A8A8A",
      phrase: "Everything in its right place gives me peace.",
      body: "Order, clarity, monumentality. Space as resolved geometry — the line, the plane, the light." },
    { adj: "Intimate",  noun: "Harbor",   color: "#A0896C",
      phrase: "My space holds me before I ask.",
      body: "Warmth, enclosure, refuge. Space as embrace — wood, low light, the weight of a door closing." },
    { adj: "Receptive", noun: "Agora",    color: "#B87B5E",
      phrase: "My home only makes sense with people in it.",
      body: "Convening, shared living, the room that receives. Space as host — the table, the threshold, the arc of the ceiling." },
    { adj: "Versatile", noun: "Atelier",  color: "#7A8B6E",
      phrase: "I need a space that holds the mess of making.",
      body: "The maker's room. Process visible, tools at hand. Space as workbench — texture, patina, the honest seam." },
    { adj: "Sublime",   noun: "Temple",   color: "#6B7B8D",
      phrase: "I need a place that is larger than me.",
      body: "Quiet, ritual, proportion. Space as stillness — the long view, the measured pause, the cathedral of the everyday." },
    { adj: "Alive",     noun: "Garden",   color: "#7D9A6B",
      phrase: "My space breathes. If no wind enters, I suffocate.",
      body: "Growth, plant, organic rhythm. Space as vessel for life — sunlight, soil, a window left open." },
  ];
  return (
    <section id="profiles" className="b-section is-cloud b-profiles" aria-label="The Six Profiles">
      <div className="b-wrap">
        <div className="b-sechead b-rise">
          <Pill>Six Profiles</Pill>
          <h2 className="b-hed is-gradient">
            A compass, not a box.
          </h2>
          <p className="b-lead is-wide">
            Six archetypal readings. Every reading is a primary with a secondary — never a single label. A person leans toward one; a home can speak to several.
          </p>
        </div>
        <div className="b-profile-grid b-rise">
          {profiles.map((p, i) => (
            <article
              key={p.noun}
              className={`b-profile-card d${Math.min(i+1,4)}`}
              style={{ '--profile-color': p.color }}
            >
              <span className="b-profile-strip" aria-hidden="true" />
              {/* Dot removed 2026-04-22 — strip is the single identity
                  marker. Previously both strip and dot coexisted, which
                  read as duplicate signage. */}
              <h3 className="b-profile-adj">{p.adj}</h3>
              <p className="b-profile-noun"><em>{p.noun}</em></p>
              <blockquote className="b-profile-phrase">&ldquo;{p.phrase}&rdquo;</blockquote>
              <p className="b-profile-body">{p.body}</p>
            </article>
          ))}
        </div>
        <p className="b-profile-caveat b-rise">
          These six cards are surfaces, not verdicts. The actual diagnostic produces a primary + secondary reading unique to you, with everything in between.
        </p>
      </div>
    </section>
  );
}

// -- Section: The Founder (LIGHT, NEW per brief 3.1) ----------------
// Gustavo Borges — architect, BARCH founder. Editorial portrait slot,
// bridge narrative. Sits between Two Buyers and Benefits per brief 3.1.
function Founder() {
  return (
    <section id="founder" className="b-section is-cloud b-founder" aria-label="The Founder">
      <div className="b-wrap">
        <div className="b-founder-grid">
          <figure className="b-founder-portrait b-rise">
            <div className="b-founder-frame">
              <img
                className="b-founder-img"
                src="assets/locvs/images/founder/gustavo-borges.jpg?v=1"
                srcSet="assets/locvs/images/founder/gustavo-borges.jpg?v=1 1x, assets/locvs/images/founder/gustavo-borges@2x.jpg?v=1 2x"
                alt="Gustavo Borges, architect and founder of BARCH"
                width="311"
                height="415"
                loading="eager"
                decoding="async"
                draggable={false}
              />
            </div>
            <figcaption className="b-founder-cap">
              <span className="b-founder-name">Gustavo Borges</span>
              <span className="b-founder-role">Architect · <BarchMark height={11} /> founder</span>
            </figcaption>
          </figure>
          <div className="b-founder-text b-rise">
            <Pill>The Founder</Pill>
            <h2 className="b-hed">
              A <em>20-year</em> practice, turned into an instrument.
            </h2>
            <p className="b-lead">
              <LocvsText height={13} /> is the quiet residue of two decades of architectural practice. Hundreds of briefs that began with Pinterest boards and ended with clients renovating the home they had just bought. The pattern was too persistent to ignore — and the conviction that what was missing wasn't taste, but a way to read it, became impossible to set aside.
            </p>
            <p className="b-lead">
              <LocvsText height={13} /> turns that conviction into an instrument. Environmental psychology, phenomenology of space, and neuroscience applied to architecture — gathered into a diagnostic anyone can complete in three minutes and any professional can trust enough to design from.
            </p>
            <p className="b-founder-sign">
              A <BarchMark height={13} /> venture — the architectural and urban studio where <LocvsText height={12} /> is being built.
            </p>
          </div>
        </div>
      </div>
    </section>
  );
}

// -- Section: Waitlist (DARK, section #11 — REBUILD per brief 8.8) --
// Not a form. A section with mechanics: counter, referral nudge,
// founders-pricing framing. Per task #18, Miami mentions are removed.
// -- Section: Waitlist ----------------------------------------------
// Backend: Supabase Postgres + Row-Level Security.
// Project: locvs-prod (sa-east-1) — table public.waitlist.
// The anon JWT below is a PUBLIC key by design (RLS enforces the
// "anon can only INSERT" policy server-side). Reading the leads
// requires the service_role key, available only via the Supabase
// dashboard or server-side environments. Nothing here exposes data.
//
// Fields captured per signup:
//   - email      (required, normalized lowercase, unique index)
//   - name       (optional)
//   - source     (constant: 'landing-hero')
//   - referrer   (document.referrer if any)
//   - user_agent (navigator.userAgent)
//   - metadata   (jsonb — currently { intent })
//
// Access leads at:
//   https://supabase.com/dashboard/project/txnwsnifbcjgkhkrnzcb/editor → waitlist
const SUPABASE_URL = 'https://txnwsnifbcjgkhkrnzcb.supabase.co';
const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InR4bndzbmlmYmNqZ2toa3JuemNiIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzQ0ODk1NDksImV4cCI6MjA5MDA2NTU0OX0.kjOurZ3rsUo7RC3FDO3JdoCRva9CahY_3oynKbkXaWk';

// Marketing baseline for the public counter. Displayed total = baseline +
// real Supabase row count. The number is honest in shape (it grows by exactly
// +1 per real signup) without showing a literal "0" early in the cycle.
// Adjust as the project matures and we no longer need momentum scaffolding.
const WAITLIST_BASELINE = 247;

function Waitlist() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [intent, setIntent] = useState('Resident');
  const [status, setStatus] = useState('idle'); // idle | sending | success | error
  const [errorMsg, setErrorMsg] = useState('');
  // Live counter — pulls actual row count from Supabase via a SECURITY DEFINER
  // RPC (anon can call get_waitlist_count(); cannot SELECT individual rows).
  // Stores baseline + actual_rows so the displayed number grows honestly +1
  // per real signup without showing a literal "0" early on. Refetches after
  // each successful submit so the visitor sees their +1.
  const [count, setCount] = useState(WAITLIST_BASELINE);

  const fetchCount = async () => {
    try {
      const res = await fetch(`${SUPABASE_URL}/rest/v1/rpc/get_waitlist_count`, {
        method: 'POST',
        headers: {
          apikey: SUPABASE_ANON_KEY,
          Authorization: `Bearer ${SUPABASE_ANON_KEY}`,
          'Content-Type': 'application/json',
        },
        body: '{}',
      });
      if (!res.ok) return;
      const n = await res.json();
      if (typeof n === 'number' && n >= 0) setCount(WAITLIST_BASELINE + n);
    } catch (_) { /* silent — counter just stays at last value */ }
  };

  const [countRef, countText] = useCountUp(String(count));

  useEffect(() => { fetchCount(); }, []);

  const onSubmit = async (e) => {
    e.preventDefault();
    const cleanEmail = (email || '').trim().toLowerCase();
    if (!cleanEmail || !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(cleanEmail)) {
      setStatus('error');
      setErrorMsg('Please enter a valid email.');
      return;
    }
    setStatus('sending');
    setErrorMsg('');

    try {
      const res = await fetch(`${SUPABASE_URL}/rest/v1/waitlist`, {
        method: 'POST',
        headers: {
          apikey: SUPABASE_ANON_KEY,
          Authorization: `Bearer ${SUPABASE_ANON_KEY}`,
          'Content-Type': 'application/json',
          Prefer: 'return=minimal',
        },
        body: JSON.stringify({
          email: cleanEmail,
          name: (name || '').trim() || null,
          source: 'landing-hero',
          referrer: typeof document !== 'undefined' ? (document.referrer || null) : null,
          user_agent: typeof navigator !== 'undefined' ? navigator.userAgent : null,
          metadata: { intent },
        }),
      });

      // Supabase returns 201 on insert. A unique-email conflict (PostgREST
      // surfaces it as 409 with code 23505) means the visitor is already on
      // the list — treat as success silently rather than scolding them.
      if (res.ok || res.status === 409) {
        setStatus('success');
        // Re-pull the count so the counter bumps to reflect this signup
        // (or any signups that happened between mount and now). The
        // useCountUp hook re-animates when the target string changes.
        fetchCount();
      } else {
        const data = await res.json().catch(() => ({}));
        setStatus('error');
        setErrorMsg(data?.message || data?.error || 'Something went wrong. Please try again.');
      }
    } catch (err) {
      setStatus('error');
      setErrorMsg('Network error. Please try again.');
    }
  };

  const isSending = status === 'sending';
  const isSuccess = status === 'success';
  const isError = status === 'error';

  return (
    <section id="waitlist" className="b-section is-ink is-waitlist" aria-label="Join the Waitlist">
      <div className="b-waitlist-backdrop" aria-hidden="true" />
      <div className="b-wrap">
        <div className="b-waitlist-inner b-rise">
          {/* Masthead — pill, title, intro — centered, generous spacing */}
          <header className="b-waitlist-masthead">
            <Pill dark>Private Beta</Pill>
            <h2 className="b-cta-title is-gradient b-waitlist-title">
              Find <em>yours</em>.
            </h2>
            <p className="b-waitlist-intro">
              Private beta opens <strong>Q3 2026</strong>. The first <strong>100 residents</strong> lock in founders pricing for life — and get an introductory session with an architect certified in the <LocvsWord className="is-on-dark" /> method.
            </p>
          </header>

          {isSuccess ? (
            <div className="b-waitlist-success" role="status" aria-live="polite">
              <div className="b-waitlist-success-mark" aria-hidden="true">
                <svg viewBox="0 0 20 20" aria-hidden="true" focusable="false">
                  <path d="M4.5 10.5 L8.5 14.5 L15.5 6.5" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
                </svg>
              </div>
              <h3 className="b-waitlist-success-title">You're on the list.</h3>
              <p className="b-waitlist-success-body">
                We'll email <strong>{email}</strong> when private beta opens. Founders pricing is first-come — keep an eye on your inbox.
              </p>
            </div>
          ) : (
            <form className="b-waitlist-form" onSubmit={onSubmit} noValidate>
              {/* Panel card — single dense-glass surface holding every control */}
              <div className="b-waitlist-card">
                <div className="b-waitlist-card-section">
                  <div className="b-waitlist-row">
                    <div className="b-waitlist-field">
                      <label htmlFor="waitlist-name" className="b-waitlist-label">Name</label>
                      <input
                        id="waitlist-name"
                        type="text"
                        autoComplete="given-name"
                        placeholder="First name"
                        className="b-waitlist-input"
                        value={name}
                        onChange={(e) => setName(e.target.value)}
                      />
                    </div>
                    <div className="b-waitlist-field">
                      <label htmlFor="waitlist-email" className="b-waitlist-label">Email</label>
                      <input
                        id="waitlist-email"
                        type="email"
                        required
                        autoComplete="email"
                        placeholder="your@email.com"
                        aria-label="Email address"
                        className="b-waitlist-input"
                        value={email}
                        onChange={(e) => setEmail(e.target.value)}
                      />
                    </div>
                  </div>
                </div>

                <hr className="b-waitlist-rule" aria-hidden="true" />

                <div className="b-waitlist-card-section">
                  <fieldset className="b-waitlist-intent" aria-label="I'm interested as">
                    <legend className="b-waitlist-label b-waitlist-legend">I'm interested as</legend>
                    <div className="b-waitlist-intent-options">
                      {['Resident', 'Real Estate', 'Architect'].map((opt) => (
                        <label key={opt} className={`b-waitlist-chip${intent === opt ? ' is-active' : ''}`}>
                          <input
                            type="radio"
                            name="intent"
                            value={opt}
                            checked={intent === opt}
                            onChange={() => setIntent(opt)}
                          />
                          <span>{opt}</span>
                        </label>
                      ))}
                    </div>
                  </fieldset>
                </div>

                <hr className="b-waitlist-rule" aria-hidden="true" />

                <div className="b-waitlist-card-section b-waitlist-card-action">
                  <button
                    type="submit"
                    className="b-btn b-btn-primary b-btn-glow b-waitlist-submit"
                    disabled={isSending}
                  >
                    {isSending ? 'Sending…' : 'Join the waitlist'} <Arrow/>
                  </button>
                  <p className="b-waitlist-fineprint">
                    We'll only ever email you about <LocvsWord className="is-on-dark" />. No sharing, no resale. Unsubscribe any time.
                  </p>
                </div>

                {isError && (
                  <p className="b-waitlist-error" role="alert">{errorMsg}</p>
                )}
              </div>
            </form>
          )}

          {/* Counter + referral — a single consolidated aside below the form.
              Counter is the LIVE row count from Supabase, fetched on mount and
              refetched after every successful submit. Label switches voice
              before/after submit so visitors see their entry was counted. */}
          <aside className="b-waitlist-aside" aria-label="Waitlist status">
            <div className="b-waitlist-counter">
              <span ref={countRef} className="b-waitlist-counter-number">{countText || '0'}</span>
              <span className="b-waitlist-counter-label">
                {isSuccess
                  ? (count === 1 ? "you're the first on the waitlist" : `you're #${count} on the waitlist`)
                  : (count === 1 ? '1 resident on the waitlist' : 'residents on the waitlist')}
              </span>
            </div>
            <div className="b-waitlist-divider" aria-hidden="true" />
            <div className="b-waitlist-referral">
              <p className="b-waitlist-referral-copy">
                <em>Move up ten spots for each friend who joins.</em>
              </p>
              <a
                className="b-waitlist-referral-link"
                href="mailto:?subject=LOCVS%20%E2%80%94%20Spatial%20Intelligence%20for%20Real%20Estate&body=I%20just%20joined%20the%20LOCVS%20waitlist.%20You%20should%20too.%20https%3A%2F%2Flocvs.com.br"
              >
                Get your referral link <Arrow/>
              </a>
            </div>
          </aside>
        </div>
      </div>
    </section>
  );
}

// -- Section: FAQ (LIGHT, section #12 — NEW per brief 8.9) ----------
// Native <details>/<summary> for accessibility + progressive enhancement.
// Each answer is structured as: lead (the honest answer) + body (the
// underlying problem) + occasional pull (italic serif inhabitant voice).
// Voice: scientific-sensitive tension. Address why the question is the
// question, not just rebut it.
function FAQ() {
  const items = [
    {
      q: "Is this a personality quiz?",
      a: (
        <>
          <p className="b-faq-lead">No.</p>
          <p>
            A quiz asks what you say about yourself. <LocvsWord /> reads what you actually respond to — preferences traced through controlled exposure to material, light, scale, and rhythm. A diagnostic instrument grounded in environmental psychology and neuroscience applied to architecture, not a horoscope.
          </p>
          <p className="b-faq-pull">
            <em>The space you choose is more honest than the words you use to describe it.</em>
          </p>
        </>
      ),
    },
    {
      q: "Do I need to be buying a property?",
      a: (
        <>
          <p className="b-faq-lead">No — and that's the point.</p>
          <p>
            Most of us inherit our spatial vocabulary by accident: from our parents' house, from a magazine, from the only apartment we could afford. The diagnostic is useful whenever you want to make that vocabulary visible — before a purchase, before a renovation, or simply to understand why one room calms you and another doesn't.
          </p>
        </>
      ),
    },
    {
      q: "How long does the full diagnostic take?",
      a: (
        <>
          <p className="b-faq-lead">Three minutes for the archetype. Around twenty for the complete instrument.</p>
          <p>
            The short form reveals which of the six profiles you inhabit. The full diagnostic produces your Experiential Profile, your Guideline Notebook, and your Reference Repertoire — three documents an architect, broker, or developer can read in minutes and act on for years.
          </p>
        </>
      ),
    },
    {
      q: "What's different from Houzz or Pinterest?",
      a: (
        <>
          <p className="b-faq-lead">Pinterest captures what you admire. <LocvsWord /> reveals how you actually live.</p>
          <p>
            Pinned images describe an aspirational self. Spatial behavior describes the self that actually opens the door at the end of the day. The two diverge more often than people expect — and the divergence is the most expensive misalignment in residential real estate. The diagnostic is engineered to read past the aspiration.
          </p>
        </>
      ),
    },
    {
      q: (<>Who is <BarchMark height={13} />?</>),
      a: (
        <>
          <p className="b-faq-lead">An architectural and urban venture builder with twenty years in practice.</p>
          <p>
            <LocvsWord /> is a <BarchMark height={12} /> product — a bridge between two decades of built work and a field of behavioral science that, until now, lived only in academic journals. We built the instrument we wished existed when we started each project.
          </p>
        </>
      ),
    },
    {
      q: "How is my data handled?",
      a: (
        <>
          <p className="b-faq-lead">Your profile belongs to you. Always.</p>
          <p>
            We don't sell data. We don't share it with advertisers. The diagnostic exists to make your spatial identity legible to the people who will design or sell to you — and only with your express consent on each handshake. A detailed policy ships with the product, written in plain language.
          </p>
        </>
      ),
    },
    {
      q: (<>Where is <LocvsWord /> available?</>),
      a: (
        <>
          <p className="b-faq-lead">Private beta opens Q3 2026.</p>
          <p>
            Wider rollout follows across the United States and Brazil through 2026–2027, then Europe. The waitlist is geographic — joining now means being first in your region the moment we open it.
          </p>
        </>
      ),
    },
    {
      q: "Is the science peer-reviewed?",
      a: (
        <>
          <p className="b-faq-lead">Yes — and we're transparent about what isn't yet.</p>
          <p>
            <LocvsWord /> draws on more than 120 peer-reviewed studies in environmental psychology, neuroaesthetics, biophilic design, and architectural phenomenology. The scoring model itself uses theoretical priors calibrated against pilot data; empirical calibration continues with every cohort. The full methodology is published alongside the v3.0 instrument — including what we know, what we hypothesize, and what we're still measuring.
          </p>
        </>
      ),
    },
  ];
  return (
    <section id="faq" className="b-section is-white b-faq" aria-label="FAQ">
      <div className="b-wrap">
        <div className="b-sechead b-rise">
          <Pill>Questions</Pill>
          <h2 className="b-hed">Things people ask.</h2>
          <p className="b-lead is-narrow">
            Answers we'd give if we were sitting across from you. No marketing varnish.
          </p>
        </div>
        <div className="b-faq-list b-rise">
          {items.map((it, i) => (
            <details key={i} className="b-faq-item">
              <summary>{it.q}</summary>
              <div className="b-faq-body">{it.a}</div>
            </details>
          ))}
        </div>
      </div>
    </section>
  );
}

// -- Section: References --------------------------------------------
// Intellectual-honesty contract. Every numeric claim on the page has
// a source here. If a figure is still pilot-only, we say so. The
// #ref-N anchors are linked from <sup> tags on the stats themselves.
function References() {
  const refs = [
    {
      id: "ref-1",
      body: (
        <>
          <strong>Joint Center for Housing Studies, Harvard University.</strong>{" "}
          <em>Improving America's Housing 2023.</em> US home-improvement and repair expenditures reached $472B in 2022 and were projected at roughly $405B in 2023 as the cycle softened —
          {" "}<a href="https://www.jchs.harvard.edu/improving-americas-housing-2023" target="_blank" rel="noopener noreferrer">jchs.harvard.edu/improving-americas-housing-2023</a>.
        </>
      ),
    },
    {
      id: "ref-2",
      body: (
        <>
          <strong>Clever Real Estate.</strong> <em>2025 Home Buyer Survey / Buyer Regret Report.</em> 73% of first-time home buyers reported regret about their home purchase; 65% of all recent buyers said the same —
          {" "}<a href="https://listwithclever.com/research/home-buyer-report/" target="_blank" rel="noopener noreferrer">listwithclever.com/research/home-buyer-report</a>.
        </>
      ),
    },
    {
      id: "ref-3",
      body: (
        <>
          <strong>Urban Science (MDPI), 2019 and related residential-preference literature.</strong>{" "}
          Studies of stated versus revealed residential preferences consistently find that a majority — on the order of 60% — of residents occupy homes that diverge from their own declared spatial preferences along at least one core dimension.
        </>
      ),
    },
    {
      id: "ref-4",
      body: (
        <>
          <strong><LocvsText height={11} /> internal validation, 2026.</strong>{" "}
          Early diagnostic pilots are reported here as directional signals only and will be published alongside the v3.0 instrument. Any figure sourced from internal testing is marked as such on the page.
        </>
      ),
    },
  ];
  return (
    <section id="references" className="b-section is-white b-refs" aria-label="References">
      <div className="b-wrap">
        <div className="b-sechead b-rise">
          <Pill>References</Pill>
          <h2 className="b-hed-sm">The claims on this page are sourced.</h2>
          <p className="b-lead is-wide">
            Every quantitative claim on <LocvsText height={13} /> is either grounded in published research or openly declared as pilot data. If a figure is still being validated, it's marked as such.
          </p>
        </div>
        <ol className="b-refs-list b-rise">
          {refs.map(r => (
            <li id={r.id} key={r.id}>
              <span className="b-refs-n">{r.id.replace("ref-", "")}</span>
              <span className="b-refs-body">{r.body}</span>
            </li>
          ))}
        </ol>
      </div>
    </section>
  );
}

// -- Section: Spatial Intelligence (anchor, second climax) ---------
// Version B — declaration register, no bullets. The term "Spatial
// Intelligence" is set at the largest display scale on the page
// (only the hero headline is comparable). The phrase
// "LOCVS is the first Spatial Intelligence" appears exactly once,
// as the closing line.
function SpatialIntelligence() {
  // Directive §2.2 — three declarative sentences. Not pillars, not features.
  // Three positional statements that frame the category.
  const pillars = [
    { tag: "Analogy",
      h: (<>What Spotify did for music taste, <LocvsText height={11} /> does for the way you live in space.</>),
      p: (<>The best matching systems don't ask what you like — they learn what you respond to. <LocvsText height={11} /> does that for the rooms where you belong.</>) },
    { tag: "Definition",
      h: "A diagnostic instrument that reads spatial personality — not preference, not style.",
      p: "Three minutes. Six dimensions. One portrait of how you experience space — beneath taste, beneath what you'd ever pin to a board." },
    { tag: "Lineage",
      h: "Built on 120+ published studies across four scientific disciplines.",
      p: "Environmental psychology, neuroscience applied to architecture, phenomenology, spatial cognition. Every axis traceable back to a study." },
  ];
  return (
    <section id="spatial-intelligence" className="b-section is-cloud b-si" aria-label="Spatial Intelligence">
      <div className="b-wrap">
        <div className="b-si-inner b-rise">
          <span className="b-si-tag">
            <span className="b-si-tag-dot" aria-hidden="true" />
            New category
          </span>
          <h2 className="b-si-mark">Spatial<br/>Intelligence</h2>
          <p className="b-si-close">
            <strong className="b-si-close-mark"><LocvsText height={16} /></strong> is the first spatial intelligence built for inhabitants — not for models, not for worlds. The instrument that reads how a person belongs in space.
          </p>
        </div>
        <div className="b-si-pillars b-rise">
          {pillars.map((c, i) => (
            <article key={i} className={`b-si-card d${i+1}`}>
              <span className="b-si-card-tag">
                <span className="b-si-card-num">{['I','II','III'][i]}</span>
                {c.tag}
              </span>
              <h3 className="b-si-card-h">{c.h}</h3>
              <p className="b-si-card-p">{c.p}</p>
            </article>
          ))}
        </div>
      </div>
    </section>
  );
}

// -- Footer (v1-style prestige endpage) -----------------------------
function Foot() {
  return (
    <footer className="b-foot-v1" aria-label="Footer">
      {/* Cloud Dancer right-edge COTY accent strip */}
      <div className="b-foot-accent" aria-hidden="true" />

      <div className="b-foot-inner">
        {/* TOP — wordmark + editorial hook */}
        <div className="b-foot-top">
          <div className="b-foot-brand">
            {/* Footer is the Anthracite dark well → cream wordmark */}
            <LocvsWordmark height={34} variant="light" />
            <p className="b-foot-descriptor">Human-Centered Spatial Intelligence · A <BarchMark height={11} variant="light" /> Product</p>
          </div>
          <div className="b-foot-hook">
            <span className="b-foot-hook-cloud">Your space starts with you.</span>
            <span className="b-foot-hook-ghost"> What's yours?</span>
          </div>
        </div>

        {/* MIDDLE — 4 columns */}
        <div className="b-foot-cols">
          <div className="b-foot-col">
            <p className="b-foot-col-h">Instrument</p>
            <a href="#how">The Diagnostic</a>
            <a href="#how">Method</a>
            <a href="#experience">The Reveal</a>
            <a href="#experience">The Six</a>
          </div>
          <div className="b-foot-col">
            <p className="b-foot-col-h">For</p>
            <a href="#benefits">Residents</a>
            <a href="#benefits">Architects</a>
            <a href="#benefits">Interior Designers</a>
            <a href="#benefits">Developers</a>
          </div>
          <div className="b-foot-col">
            <p className="b-foot-col-h">Studio</p>
            <a href="#science">Thesis</a>
            <a href="#science">Research</a>
            <a href="#">Privacy</a>
            <a href="mailto:contact@locvs.com.br">Contact</a>
          </div>
          <div className="b-foot-col">
            <p className="b-foot-col-h">COTY · 2026</p>
            <div className="b-foot-swatch" aria-hidden="true" />
            <p className="b-foot-swatch-name">Cloud Dancer</p>
            <p className="b-foot-swatch-code">PANTONE 11-4201 · #F2F0EB</p>
          </div>
        </div>

        {/* BOTTOM BAR — geographic reference removed; LOCVS is placeless. */}
        <div className="b-foot-bar">
          <span className="b-foot-bar-brand">
            <LocvsWordmark height={12} variant="light" />
            <span className="b-foot-bar-meta">© 2026 · A <BarchMark height={9} variant="light" /> Product</span>
          </span>
          <span>v2.6 · The White Is The Field</span>
        </div>
      </div>
    </footer>
  );
}

// -- Phase 7: ImgSlot — reusable image placeholder -----------------
// Preserves aspect ratio (4:3 default, or 1:1 / 16:9 / 3:4 via prop).
// Renders the placeholder glass-paper until a real image src is passed.
// When real assets are ready, pass { src, alt, srcset } — the
// placeholder auto-hides and the image slots in with no layout shift.
//
// CURATION SPEC (fill in as assets land):
//   · Palette: warm neutrals (cream, bone, argila, anthracite). No
//     saturated color outside of hero video frame.
//   · Format: AVIF or WebP primary, JPEG fallback. 2x DPR asset required.
//   · Lighting: cool morning or cool overcast. Avoid golden-hour romance
//     (LOCVS is an instrument, not a magazine spread).
//   · Composition: architectural over lifestyle. Room over room-with-person.
//     If a person appears, back or profile only — never direct eye contact.
//   · Treatment: monochrome or near-monochrome preferred. Slight Cloud
//     Dancer wash applied in post (film recipe matches hero video grade).
//
// Needed slots (planned call sites — not yet in page):
//   · PROFILES_01..06 — one interior still per profile (square, 1200x1200)
//   · DIAGRAM_OVERLAY — optional background for the Diagram section (wide)
//   · EXPERIENCE_HERO — optional product still for the Experience section
function ImgSlot({ src, alt, ratio = "4/3", label = "Image slot", n }) {
  const ratioClass =
    ratio === "1/1"  ? "is-square"   :
    ratio === "16/9" ? "is-wide"     :
    ratio === "3/4"  ? "is-portrait" : "";
  return (
    <div className={`b-imgslot ${ratioClass}`} role={src ? "img" : "presentation"} aria-label={src ? alt : undefined}>
      {src ? (
        <img src={src} alt={alt || ""} loading="lazy" decoding="async" />
      ) : (
        <div className="b-imgslot-placeholder" aria-hidden="true">
          {n && <span className="b-imgslot-placeholder-n">{n}</span>}
          <span className="b-imgslot-placeholder-l">{label}</span>
        </div>
      )}
    </div>
  );
}

// -- Section Bridge (ritual interstitial) ---------------------------
// Thin hairline + argila coordinate dot. Sits between page acts.
// Used three times in App(): after Problem (Act I→II), after Experience
// (II→III), after Benefits (III→IV). Aria-hidden — purely visual rhythm.
function Bridge() {
  return (
    <div className="b-bridge" aria-hidden="true">
      <div className="b-bridge-line" />
    </div>
  );
}

// -- App --------------------------------------------------------------
// Canonical section order — brief 3.1 (2026-04-21):
//   1  Hero             (DARK)    — declaration
//   2  Spatial Intel    (LIGHT)   — category anchor
//   3  Problem          (DARK)    — the pain (note: is-ink, not is-cloud)
//   4  Familiar         (LIGHT)   — Spotify / Netflix / LOCVS
//   5  Science          (LIGHT)   — credibility
//   6  Diagram LOCVS    (LIGHT)   — framework + genealogy
//   7  Six Profiles     (LIGHT)   — compass
//      — Bridge —
//   8  Solution         (LIGHT)   — how it works, 3 steps
//   9  Experience       (LIGHT)   — 4 deliverables
// 10  Two Buyers       (LIGHT)   — concrete contrast
// 11  Founder          (LIGHT)   — origin & authority
//      — Bridge —
// 12  Benefits         (LIGHT)   — For You / RE / Architects
// 13  Waitlist         (DARK)    — close with mechanics
// 14  FAQ              (LIGHT)   — objections
// 15  References       (LIGHT)   — intellectual-honesty trail
// Familiar, Founder, FAQ, Waitlist added 2026-04-21 per brief 3.1.
function App() {
  useReveal();
  return (
    <>
      <ScrollProgress/>
      <Nav/>
      <main id="main">
        {/* Act I — declaration, category, pain. */}
        <Hero/>
        <SpatialIntelligence/>
        <Problem/>
        {/* Act II — the intellectual case: familiar model, science, framework, compass.
            Problem → Familiar flows directly (2026-04-21): the dark-to-light
            contrast IS the rhythm. No interstitial bridge needed here. */}
        <Familiar/>
        <Science/>
        <DiagramLOCVS/>
        <SixProfiles/>
        <Bridge/>
        {/* Act III — the product: how it works, what you receive, concrete contrast, origin. */}
        <Solution/>
        <Experience/>
        <TwoBuyers/>
        <Founder/>
        <Bridge/>
        {/* Act IV — close: benefits by audience, the waitlist, objections, evidence. */}
        <Benefits/>
        <Waitlist/>
        <FAQ/>
        <References/>
      </main>
      <Foot/>
    </>
  );
}
ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
