// Home page, rebuilt to use the original site's voice & section structure

// Hero video resolution ladder, widest first. Only list basenames that really
// exist in assets/video: the picker walks this list, it never probes for a 404.
// Regenerate the files with tools/encode-hero.sh, which writes these names.
//
// Both tiers currently come off the same 640x360 master, so 1280 is a resample,
// not real detail. When a true HD master arrives, re-run the script without
// --upscale and add its tiers here.
const HERO_TIERS = [
  { w: 1280, base: 'assets/video/hero-1280' },
  { w: 640,  base: 'assets/video/hero-640' },
];

// Which tier this device actually needs. The hero is a full-bleed background
// with object-fit:cover, so the pixels it has to fill are not just the viewport
// width: in portrait the crop is driven by height instead. Mirror the .hero-media
// sizing from styles.css (min-height: clamp(520px, 74vh, 800px)) and take
// whichever dimension demands more.
//
// Worth knowing before adding tiers: that 520px floor times 16:9 means the hero
// always needs at least ~924 css px, so with today's ladder the size path lands
// on 1280 for every real device, phones included (320x568@2x already asks for
// 1387px). 640 is therefore the constrained-network tier, not the mobile tier.
// A 640 that fires on width would need a shorter hero on small screens.
const pickHeroTier = () => {
  const conn = navigator.connection || {};
  const light = HERO_TIERS[HERO_TIERS.length - 1];

  // Save-Data is an explicit "don't", and on 2g/3g a couple of MB of decoration
  // is worse than a slightly soft one. Browsers without the API (Safari,
  // Firefox) fall through to the size path and get the sharp tier.
  if (conn.saveData) return light;
  if (/2g|3g/.test(conn.effectiveType || '')) return light;

  const vw = window.innerWidth || 1280;
  const vh = window.innerHeight || 800;
  const heroH = Math.min(Math.max(520, vh * 0.74), 800);
  const cssPx = Math.max(vw, heroH * (16 / 9));

  // Cap DPR at 1.5. This sits behind a scrim at 60-90% opacity, so paying for
  // full 2x-3x retina density buys nothing anyone can see.
  const need = cssPx * Math.min(window.devicePixelRatio || 1, 1.5);

  // Smallest tier that still covers what we need, else the widest we have.
  let chosen = HERO_TIERS[0];
  for (const tier of HERO_TIERS) {
    if (tier.w >= need) chosen = tier;
  }
  return chosen;
};

const HomeHero = ({ setRoute }) => {
  const videoRef = React.useRef(null);
  // Resolved once on mount. Re-picking on resize would restart the loop for no
  // real gain, a window drag does not change what the device can show.
  const tier = React.useMemo(pickHeroTier, []);
  // React can drop the `muted` attribute, so set it as a property and kick off
  // playback here. Respect reduced-motion (leave the poster still). Autoplay
  // rejections are ignored, the poster image is the fallback.
  React.useEffect(() => {
    const v = videoRef.current;
    if (!v) return;
    const reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    if (reduce) return;
    v.muted = true;
    const p = v.play && v.play();
    if (p && p.catch) p.catch(() => {});
  }, []);

  return (
    <section style={{ borderBottom: '1px solid var(--border)' }}>
      {/* Autoplay muted looping hero background video (poster = still fallback) */}
      <div className="hero-media">
        <video
          ref={videoRef}
          className="hero-video"
          poster="assets/video/hero-poster.jpg"
          autoPlay muted loop playsInline preload="auto"
          aria-hidden="true"
        >
          <source src={`${tier.base}.webm`} type="video/webm" />
          <source src={`${tier.base}.mp4`} type="video/mp4" />
        </video>
        <div className="hero-scrim" aria-hidden="true"></div>
        <div className="page hero-content" style={{ position: 'relative', zIndex: 2 }}>
          <h1 className="hero-display" style={{ maxWidth: 1100 }}>
            Chambliss Steel Buildings.
            <br/>
            <span className="hero-line-2">High quality structures</span>
            <br/>
            <span className="hero-line-3">for your projects.</span>
          </h1>

          <div style={{ marginTop: 48, display: 'flex', gap: 10, justifyContent: 'flex-end', flexWrap: 'wrap' }}>
            <button className="btn btn-lg" onClick={() => setRoute('services')}>What we build</button>
            <button className="btn btn-primary btn-lg" onClick={() => setRoute('quote')}>
              Get a quote
              <svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M3 6 L9 6 M6 3 L9 6 L6 9" stroke="currentColor" strokeWidth="1.6"/></svg>
            </button>
          </div>
        </div>
      </div>

      {/* Three pillars (original site has these) */}
      <div className="mob-stack" style={{
        borderTop: '1px solid var(--border)',
        display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)',
      }}>
        {[
          { t: '100% Quality', d: 'We provide the best and highest quality steel products.' },
          { t: 'Best Rates', d: 'We give the best market rates.' },
          { t: '100% Satisfaction', d: 'Your satisfaction is our first priority.' },
        ].map((p, i) => (
          <div key={p.t} style={{
            padding: 32,
            borderRight: i < 2 ? '1px solid var(--border)' : 'none',
            background: 'var(--bg-elev)',
          }}>
            <h3 className="pillar-title">{p.t}</h3>
            <p style={{ marginTop: 12, fontSize: 14 }}>{p.d}</p>
          </div>
        ))}
      </div>
    </section>
  );
};

const HomeWhatWeDo = ({ setRoute }) => (
  <section className="section">
    <div className="page">
      <div className="mob-stack" style={{ display: 'grid', gridTemplateColumns: '1fr 1.8fr', gap: 80 }}>
        <div>
          <SectionEyebrow label="What we do" />
          <h2>What we do.</h2>
          <img
            src="assets/img/logo.png"
            alt="Chambliss Steel Buildings"
            style={{ width: 220, maxWidth: '100%', marginTop: 28, marginLeft: 'auto', marginRight: 'auto', display: 'block' }}
          />
          <p className="mono" style={{ marginTop: 16, fontSize: 11, color: 'var(--fg-faint)', letterSpacing: '0.08em', textAlign: 'center' }}>
            DESIGN · DELIVER · BUILD
          </p>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 24, fontSize: 20, lineHeight: 1.5 }}>
          <p style={{ color: 'var(--fg)' }}>
            At Chambliss Steel Buildings we take pride in the high-quality project concepts we develop
            for our customers. We try to find out what the customer's needs are and go above and beyond
            to ensure they get the best options available.
          </p>
          <p>
            Each structure is designed with your location in mind, whether it's a home, arena, or business.
            We are committed to supplying our customers exceptional products and professional service,
            along with their finished project within a reasonable timeframe.
          </p>
          <div>
            <button className="btn" onClick={() => setRoute('services')}>
              See what we build
              <svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M3 6 L9 6 M6 3 L9 6 L6 9" stroke="currentColor" strokeWidth="1.6"/></svg>
            </button>
          </div>
        </div>
      </div>
    </div>
  </section>
);

// The five delivery services as a spectrum: materials-only on one end, full
// turnkey on the other. Order matters, it IS the message.
const FULL_SERVICE = [
  { n: '01', t: 'Custom floor plan drafting', d: 'In-house CAD drafting turns your idea into buildable plans.' },
  { n: '02', t: 'Building supply packages', d: 'Materials only: the complete engineered steel package, delivered to your site.' },
  { n: '03', t: 'Concrete & foundations', d: 'Pads, footers, slabs, and columns, poured and finished right.' },
  { n: '04', t: 'Building erection', d: 'Experienced crews put your steel up square, plumb, and on schedule.' },
  { n: '05', t: 'Turnkey design-build', d: 'One contract from drawings to a finished building. We handle everything.' },
];

const HomeFullService = ({ setRoute }) => (
  <section className="section">
    <div className="page">
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'end', marginBottom: 48, flexWrap: 'wrap', gap: 24 }}>
        <div>
          <SectionEyebrow label="Full service" />
          <h2 style={{ maxWidth: 720 }}>From the building package<br/>to the finished build.</h2>
          <p style={{ marginTop: 24, maxWidth: 560, fontSize: 16 }}>
            Buy just the engineered materials package, or hand us the whole project,
            drafting, concrete, steel, and doors. We do as much or as little as you need.
          </p>
        </div>
        <button className="btn" onClick={() => setRoute('services')}>
          How we deliver
          <svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M3 6 L9 6 M6 3 L9 6 L6 9" stroke="currentColor" strokeWidth="1.6"/></svg>
        </button>
      </div>

      {/* Spectrum ruler: the two ends of the offer */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 16, marginBottom: 16 }}>
        <span className="mono" style={{ fontSize: 10, letterSpacing: '0.08em', color: 'var(--fg-faint)' }}>MATERIALS ONLY</span>
        <div style={{ flex: 1, height: 1, background: 'var(--border)', position: 'relative' }}>
          <svg width="10" height="10" viewBox="0 0 10 10" fill="none" style={{ position: 'absolute', right: -2, top: -4.5 }}>
            <path d="M2 1 L7 5 L2 9" stroke="var(--accent)" strokeWidth="1.4" />
          </svg>
        </div>
        <span className="mono" style={{ fontSize: 10, letterSpacing: '0.08em', color: 'var(--accent)' }}>COMPLETE TURNKEY</span>
      </div>

      {/* auto-fit rather than repeat(5, 1fr): grid tracks are min-width:auto, so
          five fixed columns could not shrink past the widest card's min-content
          (~138px: "Turnkey design-build" plus its padding). Between 641px, where
          .mob-stack stops applying, and ~800px the row ran past the viewport and
          put the whole page into a horizontal scroll. auto-fit drops to 4 then 3
          columns on the way down instead, and .mob-stack still forces one column
          below 640px. */}
      <div className="mob-stack" style={{
        display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(170px, 1fr))',
        border: '1px solid var(--border)',
        // Five cards into three or four columns leaves a trailing empty cell.
        // Matching the card background makes it read as a blank panel in the
        // row rather than a hole punched in it.
        background: 'var(--bg-card)',
      }}>
        {FULL_SERVICE.map((s, i) => (
          <div key={s.n} style={{
            padding: 24,
            borderRight: i < FULL_SERVICE.length - 1 ? '1px solid var(--border)' : 'none',
            // The turnkey cell is the flagship, it gets the accent bar.
            borderTop: i === FULL_SERVICE.length - 1 ? '2px solid var(--accent)' : '2px solid transparent',
            background: i === FULL_SERVICE.length - 1 ? 'var(--bg-elev)' : 'var(--bg-card)',
          }}>
            <span className="mono" style={{ fontSize: 11, color: 'var(--accent)' }}>{s.n}</span>
            <h3 className="pillar-title" style={{ marginTop: 14, fontSize: 16 }}>{s.t}</h3>
            <p style={{ marginTop: 12, fontSize: 13.5, lineHeight: 1.5 }}>{s.d}</p>
          </div>
        ))}
      </div>
    </div>
  </section>
);

// maxresdefault is the only 16:9 still YouTube serves at a useful size, but it
// is missing on older/low-res uploads, where hqdefault (4:3, 45px of letterbox
// top and bottom) is always there. object-fit: cover on a 16:9 box crops exactly
// those bars back off, so the fallback frames identically.
const ytThumb = (videoId) => `https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg`;
const ytThumbFallback = (e, videoId) => {
  if (e.target.dataset.fellBack) return;
  e.target.dataset.fellBack = '1';
  e.target.src = `https://i.ytimg.com/vi/${videoId}/hqdefault.jpg`;
};

// Click-to-load YouTube embed, same poster → live swap as ModelViewerFrame.
// Nothing loads from YouTube until the visitor opts in (pages stay fast, no
// third-party cookies up front); the nocookie host keeps it lean after.
// `initialLive` lets the film picker below hand over an opt-in the visitor has
// already made: it re-keys this component per video, so the next one mounts
// playing instead of asking for a second click.
const YouTubeFrame = ({ videoId, title, tag = null, initialLive = false }) => {
  const [live, setLive] = React.useState(initialLive);
  return (
    <div style={{ position: 'relative', border: '1px solid var(--border)', background: '#04141f', overflow: 'hidden', aspectRatio: '16 / 9' }}>
      {tag && <div className="corner-tag" style={{ zIndex: 2 }}>{tag}</div>}
      {live ? (
        <iframe
          src={`https://www.youtube-nocookie.com/embed/${videoId}?autoplay=1&rel=0`}
          title={title}
          allow="autoplay; fullscreen; encrypted-media; picture-in-picture"
          allowFullScreen
          style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', border: 'none' }}
        />
      ) : (
        <button
          onClick={() => setLive(true)}
          aria-label={`Play video: ${title}`}
          style={{
            position: 'absolute', inset: 0, width: '100%', height: '100%',
            border: 'none', cursor: 'pointer', padding: 0,
            background: '#04141f', color: 'var(--fg)', fontFamily: 'inherit', overflow: 'hidden',
          }}
        >
          <img
            src={ytThumb(videoId)}
            onError={(e) => ytThumbFallback(e, videoId)}
            alt=""
            style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', opacity: 0.75 }}
          />
          <div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(to top, rgba(4,20,31,0.75), rgba(4,20,31,0.15))' }}></div>
          <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 18 }}>
            <span style={{
              display: 'inline-flex', alignItems: 'center', gap: 10, padding: '13px 22px',
              border: '1px solid var(--accent)', color: 'var(--accent)',
              fontSize: 14, fontWeight: 600, letterSpacing: '0.02em', background: 'rgba(4,20,31,0.55)',
            }}>
              <svg width="14" height="16" viewBox="0 0 14 16" fill="currentColor"><path d="M0 0 L14 8 L0 16 Z"/></svg>
              Watch the build
            </span>
            <span className="mono" style={{ fontSize: 11, color: '#c8d4dc', letterSpacing: '0.06em' }}>
              STREAMED FROM YOUTUBE ON PLAY
            </span>
          </div>
        </button>
      )}
    </div>
  );
};

// The build films. [0] is the Airport Dream House episode that used to be the
// only one here, and it stays the default selection; the rest are pickable from
// the strip below the player. `meta` is the one or two mono lines shown opposite
// the heading while that film is selected, `credit` the caption under the player.
const BUILD_FILMS = [
  {
    id: 'CjKQHpM65Jw',
    title: 'Airport Dream House, Ep. 5',
    channel: 'Cleetus McFarland',
    meta: ['AIRPORT DREAM HOUSE · EP. 05', '38,000 LB HYDRAULIC HANGAR DOOR'],
    credit: 'AS FEATURED ON CLEETUS McFARLAND · YOUTUBE',
    blurb: 'Concrete columns poured, steel in the air, and a 38,000 lb hydraulic hangar door, ' +
           'this hangar home build was documented on Cleetus McFarland\'s Airport Dream House series.',
    a11y: 'Airport Dream House Ep. 5, hydraulic hangar door and concrete column pours',
  },
  {
    id: 'vpid7zEfF2Y',
    title: 'We Have Officially Broke Ground On Our New Shop!',
    channel: 'BoostedBoiz',
    meta: ['BOOSTEDBOIZ HQ · PART 01', 'SITE WORK & GROUNDBREAKING'],
    credit: 'AS FEATURED ON BOOSTEDBOIZ · YOUTUBE',
    blurb: 'Day one on the BoostedBoiz HQ: the site gets cleared, graded and staked out for a ' +
           'ground-up shop build.',
    a11y: 'BoostedBoiz new shop, groundbreaking and site work',
  },
  {
    id: 'RKqSg_twgps',
    title: 'Building My Dream Shop — Pouring The Massive Concrete Pad!',
    channel: 'BoostedBoiz',
    meta: ['BOOSTEDBOIZ HQ · PART 02', 'SLAB & FOUNDATION POUR'],
    credit: 'AS FEATURED ON BOOSTEDBOIZ · YOUTUBE',
    blurb: 'The foundation goes in, forms set, steel tied and a full-size slab poured for the ' +
           'shop to land on.',
    a11y: 'BoostedBoiz new shop, pouring the concrete pad',
  },
  {
    id: 'c7E5DoLqqU4',
    title: 'Introducing The New BoostedBoiz HQ! (Massive Shop Build)',
    channel: 'BoostedBoiz',
    meta: ['BOOSTEDBOIZ HQ · PART 03', 'THE FINISHED SHOP'],
    credit: 'AS FEATURED ON BOOSTEDBOIZ · YOUTUBE',
    blurb: 'The finished HQ, walked end to end, clear-span steel with the shop fully moved into it.',
    a11y: 'Introducing the new BoostedBoiz HQ, finished shop walkthrough',
  },
  {
    id: 'wmSfbl67VSU',
    title: 'Welcome To Our New Shop!!! (Hurricane Rated)',
    channel: 'Cleetus McFarland',
    meta: ['FREEDOM FACTORY · NEW SHOP', 'BUILT TO HURRICANE-RATED SPEC'],
    credit: 'AS FEATURED ON CLEETUS McFARLAND · YOUTUBE',
    blurb: 'A new shop built to hurricane-rated spec, the same engineering that goes into every ' +
           'building we put up on the Gulf Coast.',
    a11y: 'Welcome to our new hurricane-rated shop',
  },
];

const ytWatchUrl = (id) => `https://www.youtube.com/watch?v=${id}`;

const HomeFeaturedBuild = () => {
  const [active, setActive] = React.useState(0);
  // The first paint still fetches nothing from YouTube. Once the visitor has
  // opted in by picking a film, switching to another shouldn't cost them a
  // second click, so from then on the player mounts already playing.
  const [consented, setConsented] = React.useState(false);
  const playerRef = React.useRef(null);
  const film = BUILD_FILMS[active];

  const pick = (i) => {
    setActive(i);
    setConsented(true);
    // The strip sits below the player, and on a phone it is a full screen below
    // it, so bring the player back into view rather than playing off-screen.
    const el = playerRef.current;
    if (el && el.scrollIntoView) {
      const reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
      el.scrollIntoView({ behavior: reduce ? 'auto' : 'smooth', block: 'center' });
    }
  };

  return (
    <section className="section">
      <div className="page">
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'end', marginBottom: 40, flexWrap: 'wrap', gap: 24 }}>
          <div>
            <SectionEyebrow label="Featured builds" />
            <h2>Watch one go up,<br/>start to finish.</h2>
            <p style={{ marginTop: 24, maxWidth: 620, fontSize: 16 }}>
              Concrete poured, steel in the air, doors hung, several Chambliss builds have been
              documented start to finish on YouTube. That same quality and professional
              workmanship goes into every Chambliss project, whatever the size.
            </p>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 4, alignItems: 'flex-end' }}>
            {film.meta.map((line) => (
              <span key={line} className="mono" style={{ fontSize: 10, color: 'var(--fg-faint)', letterSpacing: '0.08em', textTransform: 'uppercase' }}>
                {line}
              </span>
            ))}
          </div>
        </div>

        <div ref={playerRef} style={{ scrollMarginTop: 96 }}>
          <YouTubeFrame
            key={`${film.id}-${consented}`}
            videoId={film.id}
            title={film.a11y}
            initialLive={consented}
          />
        </div>

        {/* Selected film: title + its own blurb, both swap with the picker. The
            title is a video name, a label rather than a heading, so it stays in
            Geist like the portfolio and team names do. */}
        <div style={{ marginTop: 20, display: 'flex', justifyContent: 'space-between', alignItems: 'start', flexWrap: 'wrap', gap: 20 }}>
          <div style={{ maxWidth: 620 }}>
            <div style={{ fontSize: 17, fontWeight: 600, letterSpacing: '-0.015em', lineHeight: 1.3 }}>
              {film.title}
            </div>
            <p style={{ marginTop: 8, fontSize: 14.5 }}>{film.blurb}</p>
            <span className="mono" style={{ display: 'block', marginTop: 12, fontSize: 11, color: 'var(--fg-faint)' }}>
              {film.credit}
            </span>
          </div>
          <a className="btn" href={ytWatchUrl(film.id)} target="_blank" rel="noopener noreferrer">
            Watch on YouTube
            <span aria-hidden="true" style={{ opacity: 0.6, fontSize: 11 }}>↗</span>
          </a>
        </div>

        <div style={{ marginTop: 44 }}>
          <span className="mono" style={{ display: 'block', marginBottom: 14, fontSize: 11, color: 'var(--fg-faint)', letterSpacing: '0.08em', textTransform: 'uppercase' }}>
            The build log · {BUILD_FILMS.length} films
          </span>
          <div className="film-strip">
            {BUILD_FILMS.map((f, i) => (
              <button
                key={f.id}
                className={`film-card${i === active ? ' is-active' : ''}`}
                onClick={() => pick(i)}
                aria-pressed={i === active}
                aria-label={`Play: ${f.title}, ${f.channel}`}
              >
                <span className="film-card-thumb">
                  <img src={ytThumb(f.id)} onError={(e) => ytThumbFallback(e, f.id)} alt="" loading="lazy" />
                  <span className="film-card-play" aria-hidden="true">
                    <svg width="9" height="11" viewBox="0 0 14 16" fill="currentColor"><path d="M0 0 L14 8 L0 16 Z"/></svg>
                  </span>
                  {/* Nothing is streaming until the visitor opts in, so the
                      badge on the default selection has to say so honestly. */}
                  {i === active && (
                    <span className="film-card-now mono">{consented ? 'NOW PLAYING' : 'SELECTED'}</span>
                  )}
                </span>
                <span className="film-card-body">
                  <span className="film-card-title">{f.title}</span>
                  <span className="film-card-chan mono">{f.channel}</span>
                </span>
              </button>
            ))}
          </div>
        </div>
      </div>
    </section>
  );
};

const HomeBuildings = ({ setRoute }) => {
  // Each card carries its own media: `clip` (a basename under assets/video,
  // looped on hover) where we have drone footage of that building type, `img`
  // otherwise. Three of the six are live, the rest stay stills.
  const types = [
    { t: 'Agriculture', d: 'Barns, hay storage, equipment cover. Built for working farms.',
      img: 'assets/img/IMG_0412.jpeg' },
    { t: 'Commercial', d: 'Warehouses, shops, retail, mini-storage, light industrial.',
      clip: 'ranch-workshop' },
    { t: 'Aviation', d: 'Aircraft hangars with bi-fold or hydraulic doors.',
      clip: 'hangar-aircraft' },
    // Nothing in the library is actually equestrian, no arena, stable, or horse.
    // The red barn is the closest read, and it beats the previous photo here,
    // which was a hangar with a light aircraft parked in it.
    { t: 'Equestrian', d: 'Riding arenas, stables, run-ins, clear span.',
      img: 'assets/img/image6.jpeg' },
    { t: 'Barndominiums', d: 'Live in the steel. Residential envelope, fully customized.',
      clip: 'barndominium-fog' },
    { t: '& so much more', d: 'Churches, breweries, gymnasiums, casinos, schools, fire stations.',
      img: 'assets/img/IMG_3079-1-e1682630586755.png' },
  ];
  return (
    <section className="section">
      <div className="page">
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'end', marginBottom: 48, flexWrap: 'wrap', gap: 24 }}>
          <div>
            <SectionEyebrow label="Capabilities" />
            <h2>Steel buildings to fit<br/>your needs.</h2>
          </div>
          <button className="btn" onClick={() => setRoute('services')}>
            All capabilities
            <svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M3 6 L9 6 M6 3 L9 6 L6 9" stroke="currentColor" strokeWidth="1.6"/></svg>
          </button>
        </div>

        <div className="mob-stack" style={{
          display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 0,
          border: '1px solid var(--border)',
        }}>
          {types.map((t, i) => (
            <button
              key={t.t}
              onClick={() => setRoute('services')}
              className="lift"
              style={{
                textAlign: 'left',
                padding: 0,
                background: 'var(--bg-card)',
                border: 'none',
                borderRight: (i % 3 !== 2) ? '1px solid var(--border)' : 'none',
                borderBottom: i < 3 ? '1px solid var(--border)' : 'none',
                color: 'var(--fg)',
                cursor: 'pointer',
                fontFamily: 'inherit',
              }}
            >
              {t.clip ? (
                <LoopVideo
                  src={t.clip}
                  label={`${t.t}, project footage`}
                  trigger="hover"
                  height={220}
                  style={{ border: 'none', borderBottom: '1px solid var(--border)' }}
                />
              ) : (
                <ImgSlot label={`${t.t}, project photo`} tag={null} height={220} style={{ border: 'none', borderBottom: '1px solid var(--border)' }} src={t.img} />
              )}
              <div style={{ padding: 24, display: 'flex', flexDirection: 'column', gap: 8 }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
                  <h3 className="cap-card-title">{t.t}</h3>
                  <span className="mono" style={{ fontSize: 11, color: 'var(--accent)' }}>↗</span>
                </div>
                <p style={{ fontSize: 14, marginTop: 4 }}>{t.d}</p>
              </div>
            </button>
          ))}
        </div>
      </div>
    </section>
  );
};

// The parametric designer, fully client-side three.js, no token.
// The building reshapes live as you change span/length/height/pitch/bays.
const FABTRK_DESIGNER_URL =
  (typeof window !== 'undefined' && window.FABTRK_DESIGNER_URL) ||
  'http://localhost:5173/embed/designer';

// Poster → live-embed swap. Renders a lightweight blueprint poster with a launch
// button; on click it mounts the given FabTrk <iframe> in place (loading on
// demand). Keeps pages instant on load, the 3D only spins up on intent. Used by
// the home + quote designer sections and the portfolio project cards, each
// passing its own `src`, which is required, there is no meaningful default.
const ModelViewerFrame = ({
  src,
  tag = '07 / MODEL',
  title = 'Live 3D model',
  cta = 'Launch interactive 3D model',
  sub = 'STREAMED FROM FABTRK',
  height = 'clamp(420px, 62vh, 620px)',
}) => {
  const [live, setLive] = React.useState(false);
  return (
    <div style={{ position: 'relative', border: '1px solid var(--border)', background: '#04141f', overflow: 'hidden' }}>
      {tag && <div className="corner-tag" style={{ zIndex: 2 }}>{tag}</div>}
      {live ? (
        <iframe
          src={src}
          title={title}
          scrolling="no"
          allow="xr-spatial-tracking; fullscreen"
          style={{ display: 'block', width: '100%', height, border: 'none' }}
        />
      ) : (
        <button
          onClick={() => setLive(true)}
          aria-label={cta}
          style={{
            position: 'relative', display: 'block', width: '100%',
            height, border: 'none', cursor: 'pointer',
            background: 'radial-gradient(ellipse at center, #0a2a3f 0%, #04141f 70%)',
            color: 'var(--fg)', fontFamily: 'inherit', padding: 0, overflow: 'hidden',
          }}
        >
          {/* Faint blueprint grid + truss wireframe poster art */}
          <div className="grid-bg" style={{ position: 'absolute', inset: 0, opacity: 0.35 }}></div>
          <svg viewBox="0 0 400 220" width="min(70%, 520px)" fill="none"
            style={{ position: 'absolute', left: '50%', top: '42%', transform: 'translate(-50%,-50%)', opacity: 0.9 }}>
            <g stroke="var(--accent)" strokeWidth="1.4">
              <path d="M40 180 L200 40 L360 180 Z" />
              <path d="M40 180 L360 180" />
              <path d="M90 180 L200 92 L310 180" strokeWidth="1" opacity="0.8" />
              <path d="M70 150 L330 150" strokeWidth="0.8" opacity="0.6" />
              <path d="M120 180 L120 118 M200 180 L200 92 M280 180 L280 118" strokeWidth="0.8" opacity="0.5" />
            </g>
          </svg>
          <div style={{ position: 'absolute', left: 0, right: 0, bottom: 0, top: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 18 }}>
            <span style={{
              display: 'inline-flex', alignItems: 'center', gap: 10, padding: '13px 22px',
              border: '1px solid var(--accent)', color: 'var(--accent)',
              fontSize: 14, fontWeight: 600, letterSpacing: '0.02em', background: 'rgba(4,20,31,0.55)',
            }}>
              <svg width="14" height="16" viewBox="0 0 14 16" fill="currentColor"><path d="M0 0 L14 8 L0 16 Z"/></svg>
              {cta}
            </span>
            <span className="mono" style={{ fontSize: 11, color: 'var(--fg-faint)', letterSpacing: '0.06em' }}>
              {sub}
            </span>
          </div>
        </button>
      )}
    </div>
  );
};

// Full-page interactive configurator (Phase 1). Iframes FabTrk's /embed/
// configurator/<token>; the visitor picks type/size/finish, explores the frame,
// and hits "Get this quoted", which postMessages the spec up to app.jsx, which
// routes here into the quote flow. This is the "3D configurator present on
// Chambliss" deliverable; it lives on its own route so it has room to breathe.
const ConfiguratorPage = ({ setRoute }) => (
  <div className="page-anim">
    <section style={{ borderBottom: '1px solid var(--border)' }}>
      {/* Contained header: eyebrow + title + skip action */}
      <div className="page" style={{ paddingTop: 40, paddingBottom: 24 }}>
        <SectionEyebrow id="3D" label="Configurator" />
        <h1 style={{ maxWidth: 760, margin: 0 }}>
          Design your building<br/><span style={{ color: 'var(--accent)' }}>in 3D &amp; AR.</span>
        </h1>
      </div>

      {/* Full-width configurator embed (site nav/footer stay in place) */}
      <ModelViewerFrame
        src={FABTRK_DESIGNER_URL}
        tag={null}
        title="Chambliss parametric building designer"
        cta="Launch the designer"
        sub="SET DIMENSIONS · WATCH IT RESHAPE → GET A QUOTE"
        height="clamp(560px, 82vh, 900px)"
      />

      <div style={{ height: 40 }} />
    </section>
  </div>
);

// No borderTop on the section below: .section already carries a bottom border, so
// an explicit top border doubles up against the preceding section's. The
// position: relative stays, the .grid-bg overlay is absolute against it.
const HomeModel3D = ({ setRoute }) => (
  <section className="section" style={{ position: 'relative' }}>
    <div className="grid-bg" style={{
      position: 'absolute', inset: 0, opacity: 0.25,
      maskImage: 'radial-gradient(ellipse at center, black 30%, transparent 72%)',
      WebkitMaskImage: 'radial-gradient(ellipse at center, black 30%, transparent 72%)',
    }}></div>
    <div className="page" style={{ position: 'relative' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'end', marginBottom: 40, flexWrap: 'wrap', gap: 24 }}>
        <div>
          <SectionEyebrow id="3D" label="Configurator" />
          <h2>Design your building<br/><span style={{ color: 'var(--accent)' }}>in 3D &amp; AR.</span></h2>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 4, alignItems: 'flex-end' }}>
          <span className="mono" style={{ fontSize: 10, color: 'var(--fg-faint)', letterSpacing: '0.08em', textTransform: 'uppercase' }}>
            FIG. 07 / PARAMETRIC DESIGNER
          </span>
          <span className="mono" style={{ fontSize: 10, color: 'var(--fg-faint)', letterSpacing: '0.08em' }}>
            LIVE CONFIGURATOR · REAL TIME
          </span>
        </div>
      </div>

      {/* Click-to-load: the designer is a heavy WebGL embed, so it only mounts
          after the visitor opts in. The marketing page stays fast on mobile and
          nothing streams from FabTrk until asked. */}
      <ModelViewerFrame
        src={FABTRK_DESIGNER_URL}
        tag={null}
        title="Chambliss parametric building designer"
        cta="Launch the designer"
        sub="SET DIMENSIONS · WATCH IT RESHAPE → GET A QUOTE"
      />

      <div style={{
        marginTop: 14, display: 'flex', justifyContent: 'space-between',
        alignItems: 'center', flexWrap: 'wrap', gap: 16,
      }}>
        <span className="mono" style={{ fontSize: 11, color: 'var(--fg-faint)' }}>
          SET DIMENSIONS · WATCH IT RESHAPE · POWERED BY FABTRK
        </span>
        <button className="btn btn-primary" onClick={() => setRoute('configurator')}>
          Open the full configurator
          <svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M3 6 L9 6 M6 3 L9 6 L6 9" stroke="currentColor" strokeWidth="1.6"/></svg>
        </button>
      </div>
    </div>
  </section>
);

const HomeWhoWeAre = ({ setRoute }) => (
  <section className="section" style={{ position: 'relative' }}>
    <div className="grid-bg" style={{
      position: 'absolute', inset: 0, opacity: 0.3,
      maskImage: 'radial-gradient(ellipse at center, black 30%, transparent 70%)',
      WebkitMaskImage: 'radial-gradient(ellipse at center, black 30%, transparent 70%)',
    }}></div>
    <div className="page" style={{ position: 'relative' }}>
      <div className="mob-stack" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 64, alignItems: 'center' }}>
        {/* The old alt here said "Owners & crew on site" over a photo of an empty
            building. No group shot of the owners exists, so the alt now just
            describes the finished hangar it actually is. */}
        <ImgSlot
          label="Finished aircraft hangar with a light aircraft parked inside, doors open"
          height={460}
          src="assets/img/image3-1-e1729527896326.jpeg"
        />
        <div>
          <SectionEyebrow label="Who we are" />
          <h2 style={{ maxWidth: 520 }}>Who we are.</h2>
          <p style={{ marginTop: 32, fontSize: 20, lineHeight: 1.5, color: 'var(--fg)', maxWidth: 520 }}>
            Chambliss Steel Buildings is a family-owned and operated business located in
            <span style={{ color: 'var(--accent)' }}> Live Oak, Florida</span>. We strive to make
            your experience smooth from start to finish, from design, to delivery, to build.
          </p>
          <div style={{ marginTop: 32, display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'center' }}>
            <span className="pill"><span className="led"></span>Family-owned</span>
            <span className="pill">Est. Live Oak, FL</span>
            <span className="pill">CBC1267224</span>
          </div>
          <div style={{ marginTop: 24 }}>
            <button className="btn" onClick={() => setRoute('about')}>
              About us
              <svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M3 6 L9 6 M6 3 L9 6 L6 9" stroke="currentColor" strokeWidth="1.6"/></svg>
            </button>
          </div>
        </div>
      </div>
    </div>
  </section>
);

const HomeTestimonials = () => {
  const quotes = [
    { q: 'Great company. They have a fantastic crew, and the owners go the extra mile to make sure you\'re a happy customer. Highly recommend them for your project.', who: 'Dana R.' },
    { q: 'I would HIGHLY recommend Chambliss to anyone looking for a metal building. They were up front and honest about every step of the process, which we didn\'t get from another company. Love the job they done for us!!', who: 'Eleni N.' },
    { q: 'Brad and the whole crew were amazing. Delivered on everything I was promised and then some. I couldn\'t imagine going anywhere else. Great people.', who: 'Travis N.' },
  ];
  return (
    <section className="section">
      <div className="page">
        <div style={{ marginBottom: 56 }}>
          <SectionEyebrow label="Testimonials" />
          <h2>What our customers say.</h2>
        </div>
        <div className="mob-stack" style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 24 }}>
          {quotes.map((qt, i) => (
            <div key={i} className="spec-card lift">
              <div className="mono" style={{ fontSize: 12, color: 'var(--accent)', marginBottom: 16 }}>★★★★★</div>
              <p style={{ fontSize: 15, color: 'var(--fg)', lineHeight: 1.5 }}>"{qt.q}"</p>
              <div style={{ marginTop: 24, paddingTop: 16, borderTop: '1px solid var(--border)' }}>
                <span style={{ fontSize: 13, fontWeight: 500 }}>{qt.who}</span>
              </div>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
};

const HomeCTA = ({ setRoute }) => (
  <section style={{ borderBottom: '1px solid var(--border)' }}>
    <div className="page home-cta-page" style={{ padding: '96px 32px' }}>
      <div className="home-cta-card" style={{
        border: '1px solid var(--border)', background: 'var(--bg-card)',
        padding: 64, position: 'relative', overflow: 'hidden',
      }}>
        <div className="grid-bg" style={{ position: 'absolute', inset: 0, opacity: 0.4 }}></div>
        <div className="mob-stack" style={{ position: 'relative', display: 'grid', gridTemplateColumns: '2fr 1fr', gap: 48, alignItems: 'center' }}>
          <div>
            <SectionEyebrow label="Ready to get started?" />
            <h2 style={{ maxWidth: 600 }}>
              Ready to get started? Reach out for a free quote.
            </h2>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
            <button className="btn btn-primary btn-lg" onClick={() => setRoute('quote')} style={{ justifyContent: 'space-between' }}>
              Get a quote today
              <svg width="14" height="14" viewBox="0 0 12 12" fill="none"><path d="M3 6 L9 6 M6 3 L9 6 L6 9" stroke="currentColor" strokeWidth="1.6"/></svg>
            </button>
            <a className="btn btn-lg" href="tel:+13866881121" style={{ justifyContent: 'space-between' }}>
              <span className="mono">+1 386.688.1121</span>
              <span style={{ color: 'var(--fg-faint)', fontSize: 12 }}>Mon–Fri 8–5</span>
            </a>
          </div>
        </div>
      </div>
    </div>
  </section>
);

// The eyebrow numbers on each section read as a running index down the page, so
// they have to match this order. Renumber them alongside any reshuffle here.
const Home = ({ setRoute }) => (
  <div className="page-anim">
    <HomeHero setRoute={setRoute} />
    <HomeWhatWeDo setRoute={setRoute} />
    <HomeFullService setRoute={setRoute} />
    <HomeFeaturedBuild />
    <HomeBuildings setRoute={setRoute} />
    <HomeModel3D setRoute={setRoute} />
    <HomeWhoWeAre setRoute={setRoute} />
    <HomeTestimonials />
    <HomeCTA setRoute={setRoute} />
  </div>
);

Object.assign(window, { Home, ConfiguratorPage });
