﻿/* Sections — atomic UI pieces. Exported to window for app.jsx. */

const { useEffect, useRef, useState } = React;
void useState; // referenced in ShippedTitles

/* -------------------- Top Bar -------------------- */
function TopBar() {
  return (
    <header className="topbar">
      <div className="topbar__brand">
        <span className="dot"></span>
        <span>RUBEN TAVARES · ENV / TECH ART</span>
      </div>
      <nav className="topbar__nav">
        <a href="#tech-art">Tech Art</a>
        <a href="#case-studies">Case Studies</a>
        <a href="#shipped-titles">Shipped Titles</a>
        <a href="#resume">Credits</a>
        <a href="#about">About</a>
        <a href="#contact">Contact</a>
      </nav>
      <div className="topbar__meta">
        <span className="pulse">Open to Senior Environment &amp; Tech Art roles · 2026</span>
      </div>
    </header>);

}

/* -------------------- Hero -------------------- */
function Hero() {
  const plateRef = useRef(null);
  const layerRef = useRef(null);

  useEffect(() => {
    function onScroll() {
      if (!plateRef.current || !layerRef.current) return;
      const r = plateRef.current.getBoundingClientRect();
      const vh = window.innerHeight;
      const progress = Math.max(-1, Math.min(1, (r.top + r.height / 2 - vh / 2) / vh));
      layerRef.current.style.transform = `translate3d(0, ${progress * -18}px, 0) scale(1)`;
    }
    window.addEventListener("scroll", onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener("scroll", onScroll);
  }, []);

  return (
    <section className="hero" data-screen-label="Hero">
      <div className="hero__meta">
        <div>Role <b>Senior Environment Artist & Technical Artist</b></div>
        <div>Discipline <b>Tech Art · Houdini · Shaders</b></div>
        <div>Tenure <b>30+ years · AAA</b></div>
        <div>Based <b>Open to relocation</b></div>
      </div>
      <h1 className="hero__title">
        <span className="tok">{`// senior_env_artist_ii_&_technical_artist`}</span><br />
        Production environments, procedural tools, and the systems that connect them.
      </h1>
      <div className="hero__sub">
        <p className="hero__lede">
          AAA environment art for <strong>Rockstar Games</strong>, Senior Technical Artist work at <strong>LiveLike</strong>, and procedural building workflow experience from <strong>EA</strong>.
        </p>
        <div className="hero__scroll">
          <span>Scroll · Selected work</span>
          <div className="line"></div>
        </div>
      </div>
      <div className="hero__plate" ref={plateRef}>
        <span className="corner tl"></span><span className="corner tr"></span>
        <span className="corner bl"></span><span className="corner br"></span>
        <div className="hero__plate__layer" ref={layerRef}>
          <img className="hero__image" src="/main_hero.png" alt="Selected shipped titles and technical art work" />
        </div>
      </div>
    </section>);

}

/* -------------------- Marquee -------------------- */
function Marquee() {
  const groups = [
  { label: "discipline", items: ["environment art", "tech art", "tooling", "shader authoring"] },
  { label: "tools", items: ["houdini 20", "unity hdrp", "substance", "maya"] },
  { label: "languages", items: ["python", "c#", "hlsl", "vex"] }];

  return (
    <div className="chips">
      {groups.map((g, gi) =>
      <React.Fragment key={gi}>
          <span className="chips__label">{g.label}/</span>
          {g.items.map((it, i) =>
        <span className="chip" key={i}>
              <span className="chip__dot"></span>{it}
            </span>
        )}
        </React.Fragment>
      )}
    </div>);

}

/* -------------------- Works -------------------- */
const works = [
{ id: "building-tool", num: "01", title: "Procedural Building Authoring Tool", meta: "Houdini HDA / Python Viewer State", tag: "Direct Manipulation", size: "third", placeholder: "BUILDING TOOL - VIEWPORT AUTHORING", image: "/media/Personal/technical_art/houdini/building_generator/hero_01.jpg", href: "#case-studies" },
{ id: "road-system", num: "02", title: "Procedural Road Generator", meta: "Houdini procedural workflow", tag: "Road Networks", size: "third", placeholder: "PROCEDURAL ROAD GENERATOR", image: "/media/Personal/technical_art/houdini/road_generator/hero_01.jpg", href: "#case-studies" },
{ id: "ramp-road-terrain", num: "03", title: "Houdini Ramp, Road & Terrain Interoperability in Unity", meta: "Procedural asset generation", tag: "SOP Tooling", size: "third", placeholder: "ROAD / TERRAIN PROCEDURAL TOOL", image: "/media/Personal/technical_art/houdini/ramp_road_terrain/hero_01.jpg", href: "#case-studies", logoRight: "/media/logo_unity.png" }];

const TECH_ART_CASES = [
  {
    id: "building-tool",
    num: "01",
    tab: "Building Tool",
    project: "Procedural Building Authoring Tool",
    token: "// case_study/procedural_building_authoring_tool",
    headline: "a building tool that feels hand-edited, but stays procedural.",
    subtitle: "Procedural Building Authoring Tool - Case Study",
    role: "Technical Artist / Tool Designer / Procedural Systems",
    context: "Independent R&D based on AAA procedural building workflow lessons",
    stack: ["Houdini HDA", "Python Viewer State", "PythonModule", "VEX / SOPs", "JSON state", "Unreal validation planned"],
    focus: ["Direct manipulation", "Semantic component attachment", "Performance batching", "Artist-facing UX"],
  },
  {
    id: "road-system",
    num: "02",
    tab: "Road Generator",
    project: "Procedural Road Generator",
    token: "// tech_art/procedural_road_generator",
    headline: "curve-authored roads, solved as procedural footprints.",
    subtitle: "A Houdini tool for profile-driven roads, intersections, sidewalks, curbs, lane markings, and sweep-ready boundary geometry.",
    role: "Technical Artist / Procedural Systems",
    context: "Independent Houdini R&D for road networks and city layout tools",
    stack: ["Houdini", "Python SOP", "VEX Wrangles", "PolyExpand / Boolean", "Sweep", "Attribute workflows"],
    focus: ["Road profiles", "Intersections", "Curb / sidewalk boundaries", "Curve cleanup", "Material attributes"],
    intro: "This is not just a curve-to-road sweep. The tool treats the road network as layered procedural systems.",
    hero: "/media/Personal/technical_art/houdini/road_generator/hero_01.jpg",
    futurePlans: [
      { title: "TRAFFIC LIGHTS", text: "Intersection-aware traffic-light placement with pole offsets, signal orientation, and per-road direction rules." },
      { title: "ROAD MARKINGS", text: "Procedural crosswalks, stop bars, arrows, bike-lane icons, lane dividers, and turn guides generated from intersection masks and lane attributes." },
      { title: "SIDEWALK DRESSING", text: "Randomized but rule-driven sidewalk props using road type, sidewalk width, curb proximity, and intersection context." },
      { title: "VIEWER STATE EDITING", text: "Viewport controls for lane count, lane width, sidewalk width, curb radius, intersection regions, and profile presets." },
      { title: "INTERSECTION AUTHORING", text: "Editable controls for T-junctions, 4-way intersections, corner radii, crosswalk placement, and lane continuation behavior." },
      { title: "ROAD PRESETS", text: "Preset profiles for residential streets, boulevards, alleys, bike-friendly roads, and wider urban avenues." },
    ],
    placeholders: ["Road system hero", "Curve authoring", "Lane profile controls", "Intersection generation"],
  },
  {
    id: "ramp-road-terrain",
    num: "03",
    tab: "Houdini + Unity Path Tool",
    project: "Houdini Ramp, Road & Terrain Interoperability in Unity",
    token: "// tech_art/ramp_road_terrain_tool",
    headline: "procedural terrain-connected paths built for fast layout iteration.",
    subtitle: "A curve-driven HDA workflow for generating gameplay paths, snapping procedural border assets, and deforming terrain directly inside Unity.",
    role: "Technical Artist / Procedural Systems",
    context: "Focused prototype proving a reusable procedural attachment pattern between Unity-authored curves and Houdini-generated assets",
    stack: ["Houdini HDA", "Houdini Engine", "Unity", "VEX / SOPs", "Curve tools"],
    focus: ["Gameplay path authoring", "Border snapping", "Procedural ramp profiles", "Terrain response"],
    tags: ["Unity", "Houdini Engine", "HDA", "Curve Tools", "Procedural Modeling", "Gameplay Layout", "Terrain Deformation", "VEX"],
    intro: "This prototype allowed artists and designers to draw a main gameplay path in Unity through Houdini Engine, then generate related procedural assets from that curve.",
    description: "The main path HDA acted as the source of truth. Secondary HDAs could read the generated path geometry, detect the outer edge, snap their own editable curves to that border, and sweep procedural profiles along the result. The example shown is a quarter-pipe ramp, but the same snapping pattern could support curbs, rails, barriers, ledges, sidewalks, or other procedural border assets.",
    breakdown: [
      { num: "01", title: "Main Path HDA", text: "Editable Unity curve, exposed width/resolution/cross-section controls." },
      { num: "02", title: "Border Snapping", text: "Secondary curves detect and snap to the generated path edge." },
      { num: "03", title: "Ramp Generator", text: "Sweeps a procedural ramp profile along the snapped curve." },
      { num: "04", title: "Terrain Response", text: "Terrain HDA flattens/projects areas around gameplay elements." },
      { num: "05", title: "Production Value", text: "Layout remains editable because attached assets update from the path relationship." },
    ],
    video: "/media/Personal/technical_art/houdini/ramp_road_terrain/Unity_Procedural_Asset_Generation.mp4",
    placeholders: ["Ramp tool hero", "Curve handles", "Terrain adaptation", "Unity validation"],
  },
];


function Works() {
  function selectWork(event, work) {
    event.preventDefault();
    window.dispatchEvent(new CustomEvent("tech-art-select", { detail: { id: work.id } }));
    document.getElementById("case-studies")?.scrollIntoView({ behavior: "smooth", block: "start" });
  }

  return (
    <section id="tech-art" className="section" data-screen-label="Tech Art">
      <div className="section__head">
        <div className="section__index"><b>01</b> · TECH ART</div>
        <h2 className="section__title">
          <span className="tok">{`// recent_work[]`}</span>Procedural tools, environments, and production systems.
        </h2>
      </div>
      <div className="works">
        {works.map((w) =>
        <a key={w.num} className={`work work--${w.size}`} href={w.href || "#case-studies"} onClick={(event) => selectWork(event, w)}>
            <div className="work__frame">
              <div className="work__img">
                {w.image ? (
                  <>
                    <img className="work__media" src={w.image} alt={w.title} loading="lazy" />
                    <img className="work__logo" src="/media/logo_houdini.png" alt="" aria-hidden="true" loading="lazy" />
                    {w.logoRight && <img className="work__logo work__logo--right" src={w.logoRight} alt="" aria-hidden="true" loading="lazy" />}
                  </>
                ) : (
                  <div className="placeholder"><span>{w.placeholder}</span></div>
                )}
              </div>
              <div className="work__overlay">
                <span className="work__overlay__tag">View Case Study →</span>
              </div>
            </div>
            <div className="work__caption">
              <span className="num">{w.num} ·</span>
              <span className="title">{w.title}</span>
              <span className="meta">{w.tag}<br />{w.meta}</span>
            </div>
          </a>
        )}
      </div>
    </section>);

}

/* -------------------- Case Study -------------------- */
function LegacyCaseStudy() {
  return (
    <section id="case-studies" className="section" data-screen-label="Case Studies">
      <div className="section__head">
        <div className="section__index"><b>02</b> · CASE STUDIES</div>
        <h2 className="section__title">
          <span className="tok">{`// case_study/procedural_building_authoring_tool`}</span>a building tool that feels hand-edited, but stays procedural.
        </h2>
        <div className="section__subtitle">Procedural Building Authoring Tool - Case Study</div>
      </div>
      <div className="case">
        <aside className="case__aside">
          <div>Project</div>
          <p>Procedural Building Authoring Tool</p>
          <h4>Role</h4>
          <p>Technical Artist / Tool Designer / Procedural Systems</p>
          <h4>Context</h4>
          <p>Independent R&amp;D based on AAA procedural building workflow lessons</p>
          <h4>Stack</h4>
          <ul>
            <li>Houdini 20</li>
            <li>Unity HDRP</li>
            <li>Python · C#</li>
            <li>Substance · Maya</li>
          </ul>
          <h4>Focus</h4>
          <p>Direct manipulation / semantic component attachment</p>
        </aside>

        <div className="case__body">
          <div>
            <h3><span className="num">01</span>The Problem</h3>
            <p>Procedural building tools often lose the artist the moment a generated form needs a specific edit. This project keeps the building procedural while making the viewport feel direct and tactile.</p>
            <p>The goal is a tool that keeps authored decisions live through building edits.</p>
          </div>

          <blockquote className="case__pullquote">
            This is not a generic building generator. It is a direct-manipulation procedural authoring tool.
            <span>the design constraint behind the system</span>
          </blockquote>

          <div>
            <h3><span className="num">02</span>The Approach</h3>
            <p>The Viewer State translates artist gestures into persistent procedural state while the HDA evaluates the building structure.</p>
            <p>Components attach to generated faces with semantic IDs and normalized coordinates, so they survive edits instead of becoming destructive one-offs.</p>
          </div>

          <div className="case__breakdown">
            <div className="case__breakdown__cell">
              <div className="placeholder"><span>VIEWER STATE</span></div>
              <span className="label">01 · Artist input</span>
            </div>
            <div className="case__breakdown__cell">
              <div className="placeholder"><span>HDA STATE</span></div>
              <span className="label">02 · JSON state</span>
            </div>
            <div className="case__breakdown__cell">
              <div className="placeholder"><span>BUILDING OUTPUT</span></div>
              <span className="label">03 · Generated building</span>
            </div>
          </div>

          <div>
            <h3><span className="num">03</span>The Breakdown</h3>
            <p>The procedural state tracks face, floor, normalized placement, and depth offsets so components remain live when the building changes.</p>
            <p>Performance work focuses on batching, deferred rebuilds, and navigation guards so viewport editing stays responsive.</p>
          </div>

          <div className="case__metrics">
            <div className="case__metric">
              <div className="num">face <em>id</em></div>
              <div className="lbl">Semantic attachment</div>
            </div>
            <div className="case__metric">
              <div className="num">live <em>state</em></div>
              <div className="lbl">Persistent edits</div>
            </div>
            <div className="case__metric">
              <div className="num">json <em>refs</em></div>
              <div className="lbl">JSON references</div>
            </div>
            <div className="case__metric">
              <div className="num">fast <em>mode</em></div>
              <div className="lbl">Fast interaction mode</div>
            </div>
          </div>

          <div>
            <h3><span className="num">04</span>What I Learned</h3>
            <p>The instinct on a project this size is to over‑proceduralize. The win was the opposite — proceduralism handles the 90% of the world the camera glances at, and we earned the right to spend hand‑craft budget on the 10% the player actually stops to look at. Tools are how you buy that budget back.</p>
          </div>
        </div>
      </div>
    </section>);

}

/* -------------------- Tech Art / Node Graph -------------------- */
function LegacyTechArt() {
  return (
    <section id="tech" className="section" data-screen-label="Tech Art">
      <div className="section__head">
        <div className="section__index"><b>04</b> · THE ARCHITECTURE</div>
        <h2 className="section__title">
          <span className="tok">{`// architecture`}</span>The tool separates artist gestures from procedural state.
        </h2>
      </div>
      <div className="tech">
        <div className="tech__copy">
          <h3>Procedural Building Authoring Tool - a Houdini HDA designed around direct manipulation.</h3>
          <p>The viewer state listens to handles, face picks, menu actions, and component gestures, then writes compact state back onto the HDA for procedural evaluation.</p>
          <p>The goal is a building workflow that feels editable by hand without abandoning procedural regeneration.</p>
          <div className="tech__stack">
            <div><span>Houdini</span><span>20.0</span></div>
            <div><span>Unity HDRP</span><span>2023.3</span></div>
            <div><span>Python</span><span>3.11</span></div>
            <div><span>C#</span><span>.NET 7</span></div>
            <div><span>HLSL / Shader Graph</span><span>—</span></div>
            <div><span>PDG · TOPs</span><span>—</span></div>
          </div>
        </div>
        <div className="graph">
          <div className="graph__header">
            <span>PROCEDURAL_BUILDING_TOOL · /obj/building_authoring</span>
            <span>stateful viewport editing</span>
          </div>
          <svg className="graph__svg" viewBox="0 0 700 500" preserveAspectRatio="none">
            <defs>
              <marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
                <path d="M0,0 L10,5 L0,10 z" fill="var(--fg)" />
              </marker>
            </defs>
            <path d="M 130 80 C 200 80, 200 180, 290 180" stroke="var(--fg)" strokeWidth="1.5" fill="none" markerEnd="url(#arrow)" />
            <path d="M 130 200 C 200 200, 200 200, 290 200" stroke="var(--fg)" strokeWidth="1.5" fill="none" markerEnd="url(#arrow)" />
            <path d="M 130 320 C 200 320, 200 220, 290 220" stroke="var(--fg)" strokeWidth="1.5" fill="none" markerEnd="url(#arrow)" />
            <path d="M 430 200 C 500 200, 500 130, 580 130" stroke="var(--fg)" strokeWidth="1.5" fill="none" markerEnd="url(#arrow)" />
            <path d="M 430 220 C 500 220, 500 280, 580 280" stroke="var(--fg)" strokeWidth="1.5" fill="none" markerEnd="url(#arrow)" />
            <path d="M 430 240 C 500 240, 500 410, 580 410" stroke="var(--accent)" strokeWidth="1.5" fill="none" markerEnd="url(#arrow)" />
          </svg>

          <div className="node" style={{ top: '8%', left: '4%' }}>
            <div className="node__title"><b>CURVE_INPUT</b><span>SOP</span></div>
            <div className="node__body">
              <span><b>points</b> · 14</span>
              <span><b>span</b> · 220m</span>
            </div>
          </div>
          <div className="node" style={{ top: '34%', left: '4%' }}>
            <div className="node__title"><b>PAINT_MASK</b><span>VOL</span></div>
            <div className="node__body">
              <span><b>res</b> · 1024</span>
              <span><b>chan</b> · density</span>
            </div>
          </div>
          <div className="node" style={{ top: '60%', left: '4%' }}>
            <div className="node__title"><b>INTENT</b><span>ATTR</span></div>
            <div className="node__body">
              <span><b>presets</b> · 8</span>
            </div>
          </div>

          <div className="node node--accent" style={{ top: '32%', left: '40%' }}>
            <div className="node__title"><b>VIEWER_STATE</b><span>PY</span></div>
            <div className="node__body">
              <span><b>seed</b> · 2147</span>
              <span><b>density</b> · curve‑driven</span>
              <span><b>output</b> · 14,820 pts</span>
            </div>
          </div>

          <div className="node" style={{ top: '20%', right: '4%' }}>
            <div className="node__title"><b>LOD_BAKE</b><span>ROP</span></div>
            <div className="node__body">
              <span><b>tiers</b> · 4</span>
              <span><b>impostor</b> · ✓</span>
            </div>
          </div>
          <div className="node" style={{ top: '50%', right: '4%' }}>
            <div className="node__title"><b>WIND_MASK</b><span>VOP</span></div>
            <div className="node__body">
              <span><b>amp</b> · 0.12</span>
            </div>
          </div>
          <div className="node node--accent" style={{ top: '78%', right: '4%' }}>
            <div className="node__title"><b>UNITY_BRIDGE</b><span>PY</span></div>
            <div className="node__body">
              <span><b>prefab</b> · ✓</span>
              <span><b>p4</b> · checked out</span>
            </div>
          </div>
        </div>
      </div>
    </section>);

}

/* -------------------- Shipped Titles -------------------- */
const TITLES = [
{
  id: "skate",
  label: "SKATE.",
  year: "2026",
  studio: "Electronic Arts · Full Circle Studio",
  role: "Senior Environment Artist II - Procedural Technical Art Focus",
  contribution: "Procedural building generator · Houdini pipeline · urban building kits",
  slots: [
  { kind: "still", size: "hero", chip: "IMAGE", caption: "SKATE.", note: "hero", src: "/media/EA/landing_page/hero.jpg?v=20260514d" },
  { kind: "still", size: "half", chip: "IMAGE", caption: "Building Generator Kit Overview", note: "16:9", src: "/media/EA/landing_page/Untitled-1.png?v=20260514e" },
  { kind: "still", size: "half", chip: "IMAGE", caption: "Building Generator Facade Library", note: "16:9", src: "/media/EA/landing_page/IMG_6574.png?v=20260514e" },
  { kind: "still", size: "half", chip: "IMAGE", caption: "Building Generator Federalist Kits", note: "16:9", src: "/media/EA/landing_page/IMG_6552.PNG?v=20260514d" },
  { kind: "still", size: "half", chip: "IMAGE", caption: "Building Generator Federalist Kits", note: "16:9", src: "/media/EA/landing_page/IMG_6553.PNG?v=20260514d" },
  { kind: "still", size: "half", chip: "IMAGE", caption: "Building Generator Art Deco Kits", note: "16:9", src: "/media/EA/landing_page/IMG_6556.PNG?v=20260514d" },
  { kind: "still", size: "half", chip: "IMAGE", caption: "Building Generator NeoClassic Kits", note: "16:9", src: "/media/EA/landing_page/IMG_6557.PNG?v=20260514d" },
  { kind: "still", size: "half", chip: "IMAGE", caption: "Building Generator Federalist Kits", note: "16:9", src: "/media/EA/landing_page/IMG_6558.PNG?v=20260514d" },
  { kind: "still", size: "half", chip: "IMAGE", caption: "Building Generator Historical Biome", note: "16:9", src: "/media/EA/landing_page/IMG_6561.PNG?v=20260514d" },
  { kind: "still", size: "half", chip: "IMAGE", caption: "Building Generator Federalist Kits", note: "16:9", src: "/media/EA/landing_page/IMG_6564.PNG?v=20260514d" },
  { kind: "still", size: "half", chip: "IMAGE", caption: "Building Generator Kits", note: "16:9", src: "/media/EA/landing_page/IMG_6565.PNG?v=20260514d" }]

},
{
  id: "livelike",
  label: "LiveLike VR Sports",
  year: "2015-2021",
  studio: "LiveLike VR",
  role: "Senior Tech Artist",
  contribution: "VR pipeline · custom shaders · real-time environment optimization",
  logoCompany: "/media/LiveLike/landing_page/logo_company.png?v=20260514c",
  slots: [
  { kind: "still", size: "hero", chip: "IMAGE", caption: "LiveLike VR Sports", note: "hero", src: "/media/LiveLike/landing_page/hero.jpg?v=20260514d", logoProduct: "/media/LiveLike/landing_page/logo_fox.png?v=20260514c" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Fox Sports VR", note: "4:3", src: "/media/LiveLike/landing_page/fox_02.jpg?v=20260514c", logoProduct: "/media/LiveLike/landing_page/logo_fox.png?v=20260514c" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Fox Sports environment effects pass", note: "4:3", src: "/media/LiveLike/landing_page/5_fx.png?v=20260514c", logoProduct: "/media/LiveLike/landing_page/logo_fox.png?v=20260514c" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Fox Sports venue environment", note: "4:3", src: "/media/LiveLike/landing_page/fox_01.jpg?v=20260514c", logoProduct: "/media/LiveLike/landing_page/logo_fox.png?v=20260514c" },
  { kind: "video", size: "half", chip: "VIDEO", caption: "Fox dynamic lighting shaders", note: "16:9", src: "/media/LiveLike/landing_page/fox_06.mp4?v=20260514c", logoProduct: "/media/LiveLike/landing_page/logo_fox.png?v=20260514c" },
  { kind: "video", size: "half", chip: "VIDEO", caption: "Fox dynamic shader system", note: "16:9", src: "/media/LiveLike/landing_page/fox_07.mp4?v=20260514c", logoProduct: "/media/LiveLike/landing_page/logo_fox.png?v=20260514c" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Roland Garros broadcast module", note: "4:3", src: "/media/LiveLike/landing_page/rg_01.avif?v=20260514c", logoProduct: "/media/LiveLike/landing_page/logo_rg.png?v=20260514c" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Roland Garros VR suite", note: "4:3", src: "/media/LiveLike/landing_page/rg_02.jpg?v=20260514c", logoProduct: "/media/LiveLike/landing_page/logo_rg.png?v=20260514c" },
  { kind: "still", size: "third", chip: "DETAIL", caption: "TechCrunch environment material detail", note: "4:3", src: "/media/LiveLike/landing_page/tc_01.png?v=20260514c", logoProduct: "/media/LiveLike/landing_page/logo_tc.png?v=20260514c" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Oculus live sports room", note: "4:3", src: "/media/LiveLike/landing_page/oculus_01.jpg?v=20260514c", logoProduct: "/media/LiveLike/landing_page/logo_oculus.png?v=20260514c" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Fox VR match environment", note: "4:3", src: "/media/LiveLike/landing_page/fox_03.avif?v=20260514c", logoProduct: "/media/LiveLike/landing_page/logo_fox.png?v=20260514c" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Fox avatars rigging, modeling and customization system", note: "4:3", src: "/media/LiveLike/landing_page/fox_04.jpg?v=20260514c", logoProduct: "/media/LiveLike/landing_page/logo_fox.png?v=20260514c" },
  { kind: "video", size: "half", chip: "VIDEO", caption: "Fox avatar voice shader", note: "16:9", src: "/media/LiveLike/landing_page/fox_04.mp4?v=20260514c", logoProduct: "/media/LiveLike/landing_page/logo_fox.png?v=20260514c" },
  { kind: "video", size: "half", chip: "VIDEO", caption: "Fox avatar hologram VFX", note: "16:9", src: "/media/LiveLike/landing_page/fox_05.m4v?v=20260514c", logoProduct: "/media/LiveLike/landing_page/logo_fox.png?v=20260514c" }]

},
{
  id: "rdr2",
  label: "Red Dead Redemption 2",
  year: "2018",
  studio: "Rockstar Games",
  role: "Senior Environment Artist - Terrain, Photogrammetry & PBR Materials Focus",
  contribution: "Terrain vertical slices · photogrammetry workflow · PBR material standards",
  logoCompany: "/media/Rockstar/landing_page/red_dead_redemption_2/logo_company.png?v=20260514",
  logoGame: "/media/Rockstar/landing_page/red_dead_redemption_2/logo_game.png?v=20260514",
  slots: [
  { kind: "still", size: "hero", chip: "IMAGE", caption: "Red Dead Redemption 2", note: "21:9", src: "/media/Rockstar/landing_page/red_dead_redemption_2/hero.jpg?v=20260514" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Terrain vertical slice", note: "4:3", src: "/media/Rockstar/landing_page/red_dead_redemption_2/d9d0d6_0b9c2fcc8f384967ad51431065588c61~mv2.avif?v=20260514" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Frontier environment", note: "4:3", src: "/media/Rockstar/landing_page/red_dead_redemption_2/d9d0d6_277ef22a66ec4a3ea131d1a19caf434c~mv2.avif?v=20260514" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Landscape material study", note: "4:3", src: "/media/Rockstar/landing_page/red_dead_redemption_2/d9d0d6_c79ea00c16814da0aa9cbf3359c4f2d2~mv2.avif?v=20260514" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Production environment still", note: "4:3", src: "/media/Rockstar/landing_page/red_dead_redemption_2/SECRET_04.jpg?v=20260514d" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Open-world detail pass", note: "4:3", src: "/media/Rockstar/landing_page/red_dead_redemption_2/SECRET_05.jpg?v=20260514d" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Terrain material detail", note: "4:3", src: "/media/Rockstar/landing_page/red_dead_redemption_2/SECRET_065.jpg?v=20260514d" }]

},
{
  id: "gtav",
  label: "Grand Theft Auto V",
  year: "2013",
  studio: "Rockstar Games",
  role: "Senior 3D Environment Artist",
  contribution: "High-density urban blocks · LOD/HLOD systems · open-world optimization",
  logoCompany: "/media/Rockstar/landing_page/gta/logo_company.png",
  logoGame: "/media/Rockstar/landing_page/gta/logo_game.png?v=20260514b",
  slots: [
  { kind: "still", size: "hero", chip: "IMAGE", caption: "Grand Theft Auto V", note: "21:9", src: "/media/Rockstar/landing_page/gta/hero.jpg" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Urban block", note: "4:3", src: "/media/Rockstar/landing_page/gta/d9d0d6_04c085303f2348fcb0c94e3c238b1023~mv2.jpeg" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Street detail", note: "4:3", src: "/media/Rockstar/landing_page/gta/d9d0d6_0b437261db334e998b53b881ea8828bd~mv2.jpeg" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Downtown exterior", note: "4:3", src: "/media/Rockstar/landing_page/gta/d9d0d6_0bcd402a661b47fbb6c506e1721f67c5~mv2.jpeg" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "City facade", note: "4:3", src: "/media/Rockstar/landing_page/gta/d9d0d6_0ed150db57284a1ca3ba3eda18f7f7a2~mv2.jpeg" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Open-world environment", note: "4:3", src: "/media/Rockstar/landing_page/gta/d9d0d6_5e9ad841cdaa46bea8602a74146711fe~mv2.jpeg" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Urban streetscape", note: "4:3", src: "/media/Rockstar/landing_page/gta/d9d0d6_626a42bd54e9485a947ff321537f5caf~mv2.jpeg" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "City vista", note: "4:3", src: "/media/Rockstar/landing_page/gta/d9d0d6_6a377470270b4b01bbf64c94ef177e98~mv2.jpeg" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Downtown set dressing", note: "4:3", src: "/media/Rockstar/landing_page/gta/d9d0d6_95563a23b5c24c72ae5bb3c91e77af54~mv2.jpeg" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Exterior environment pass", note: "4:3", src: "/media/Rockstar/landing_page/gta/d9d0d6_990b25000f5e46d9a289d916155fabce~mv2.jpeg" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "City material pass", note: "4:3", src: "/media/Rockstar/landing_page/gta/d9d0d6_ae409fa777424cada604f985b4363b6f~mv2.jpeg" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Urban lighting", note: "4:3", src: "/media/Rockstar/landing_page/gta/d9d0d6_cd8ab281ca034ce1922e397e8582d2af~mv2.jpeg" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Street environment", note: "4:3", src: "/media/Rockstar/landing_page/gta/d9d0d6_cde1efc078f847fea5bc71d05add69bc~mv2.jpeg" },
  { kind: "still", size: "half", chip: "IMAGE", caption: "Los Santos exterior", note: "16:9", src: "/media/Rockstar/landing_page/gta/d9d0d6_d021f0fb18c0435f9ca90ae0fee31fdf~mv2.jpeg" },
  { kind: "still", size: "half", chip: "IMAGE", caption: "Environment production still", note: "16:9", src: "/media/Rockstar/landing_page/gta/d9d0d6_de87967a3e62444a9c86a195cc83bfff~mv2.jpeg" }]

},
{
  id: "rdr",
  label: "Red Dead Redemption",
  year: "2010",
  studio: "Rockstar Games",
  role: "Lighting Artist",
  contribution: "Lighting pipeline · gameplay regions · cinematic environment support",
  logoCompany: "/media/Rockstar/landing_page/red_dead_redemption/logo_company.png?v=20260514",
  logoGame: "/media/Rockstar/landing_page/red_dead_redemption/logo_game.png?v=20260514",
  slots: [
  { kind: "still", size: "hero", chip: "IMAGE", caption: "Red Dead Redemption", note: "hero", src: "/media/Rockstar/landing_page/red_dead_redemption/hero.jpg?v=20260514d" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Frontier lighting", note: "4:3", src: "/media/Rockstar/landing_page/red_dead_redemption/102.jpg?v=20260514" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Plains at dawn", note: "4:3", src: "/media/Rockstar/landing_page/red_dead_redemption/46.jpg?v=20260514" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Desert environment", note: "4:3", src: "/media/Rockstar/landing_page/red_dead_redemption/81.jpg?v=20260514" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Frontier vista", note: "4:3", src: "/media/Rockstar/landing_page/red_dead_redemption/8rrr8_2.jpg?v=20260514" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Western settlement", note: "4:3", src: "/media/Rockstar/landing_page/red_dead_redemption/d9d0d6_21e56f13e59348b1b2c1db6f368888a0~mv2.avif?v=20260514" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Landscape lighting pass", note: "4:3", src: "/media/Rockstar/landing_page/red_dead_redemption/d9d0d6_e6a52c826ce145d49c2b5a8d62302aeb~mv2.avif?v=20260514" },
  { kind: "still", size: "half", chip: "IMAGE", caption: "Escalera environment", note: "16:9", src: "/media/Rockstar/landing_page/red_dead_redemption/escalara_428.jpg?v=20260514" },
  { kind: "still", size: "half", chip: "IMAGE", caption: "MacFarlane's ranch", note: "16:9", src: "/media/Rockstar/landing_page/red_dead_redemption/macfarlanes_77.jpg?v=20260514" }]

},
{
  id: "midnightclub",
  label: "Midnight Club: Los Angeles",
  year: "2008",
  studio: "Rockstar Games",
  role: "3D Environment Artist",
  contribution: "Open-world city environments · street dressing · racing world production",
  logoCompany: "/media/Rockstar/landing_page/midnight_club/logo_company.png?v=20260514",
  logoGame: "/media/Rockstar/landing_page/midnight_club/logo_game.png?v=20260514",
  slots: [
  { kind: "still", size: "hero", chip: "IMAGE", caption: "Midnight Club: Los Angeles", note: "hero", src: "/media/Rockstar/landing_page/midnight_club/hero.jpg?v=20260514d" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Los Angeles street environment", note: "4:3", src: "/media/Rockstar/landing_page/midnight_club/35818_MidnightClubLA-32%5B1%5D.jpg?v=20260514d" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Urban racing route", note: "4:3", src: "/media/Rockstar/landing_page/midnight_club/midnight-club-los-angeles-20070822055417047%5B1%5D.jpg?v=20260514d" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "City intersection pass", note: "4:3", src: "/media/Rockstar/landing_page/midnight_club/midnight-club-los-angeles-20071121002806048%5B1%5D.jpg?v=20260514d" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Night street lighting", note: "4:3", src: "/media/Rockstar/landing_page/midnight_club/midnight-club-los-angeles-20080925115859876%5B1%5D.jpg?v=20260514d" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Downtown racing environment", note: "4:3", src: "/media/Rockstar/landing_page/midnight_club/midnight-club-los-angeles-20081020032855328%5B1%5D.jpg?v=20260514d" },
  { kind: "still", size: "third", chip: "IMAGE", caption: "Roadside set dressing", note: "4:3", src: "/media/Rockstar/landing_page/midnight_club/midnight-club-los-angeles-20081020032907140%5B1%5D.jpg?v=20260514d" },
  { kind: "still", size: "half", chip: "IMAGE", caption: "Open-world road vista", note: "16:9", src: "/media/Rockstar/landing_page/midnight_club/midnight-club-los-angeles-20081020032912687%5B1%5D.jpg?v=20260514d" },
  { kind: "still", size: "half", chip: "IMAGE", caption: "Street racing production still", note: "16:9", src: "/media/Rockstar/landing_page/midnight_club/midnight-club-los-angeles-20081020032921499%5B1%5D.jpg?v=20260514d" }]

}];


function ShippedTitles() {
  const [activeId, setActiveId] = useState(TITLES[0].id);
  const t = TITLES.find((x) => x.id === activeId) || TITLES[0];
  return (
    <section id="shipped-titles" className="section" data-screen-label="Shipped Titles">
      <div className="section__head">
        <div className="section__index"><b>04</b> · Shipped Titles · Per-title media</div>
        <h2 className="section__title">
          <span className="tok">{`// shipped_titles/by_title`}</span>Reels and stills, organized by shipped title.
        </h2>
      </div>

      <div className="gallery__bar" role="tablist">
        <span className="gallery__bar__label">title/</span>
        {TITLES.map((title) =>
        <button
          key={title.id}
          role="tab"
          aria-pressed={activeId === title.id}
          className="gallery__tab"
          onClick={() => setActiveId(title.id)}>
          
            <span>{title.label}</span>
            <span className="yr">· {title.year}</span>
          </button>
        )}
      </div>

      <div className="gallery__meta">
        <div>title<b>{t.label}</b></div>
        <div>year<b>{t.year}</b></div>
        <div>studio<b>{t.studio}</b></div>
        <div>my role<b>{t.role}</b></div>
      </div>

      <div className={`gallery__grid gallery__grid--${t.id}`}>
        {t.slots.map((s, i) =>
        <div key={`${t.id}-${i}`} className={`gslot gslot--${s.size}`}>
          <div className="gslot__visual">
            <span className={`gslot__chip${s.kind === "video" ? " gslot__chip--accent" : ""}`}>{s.chip}</span>
            {s.src ?
          s.kind === "video" ?
          <video className="gslot__media" src={s.src} muted autoPlay loop playsInline preload="metadata" /> :
          <img className="gslot__media" src={s.src} alt={s.caption} loading="lazy" /> :
          <div className="placeholder"><span>{s.note}</span></div>
          }
            {s.kind === "video" &&
          <div className="gslot__play"><div className="gslot__play__btn">▶</div></div>
          }
            {t.logoCompany && (t.logoGame || s.logoProduct) &&
          <div className="gslot__logos" aria-hidden="true">
              <img className="gslot__logo gslot__logo--company" src={t.logoCompany} alt="" />
              <img className="gslot__logo gslot__logo--game" src={s.logoProduct || t.logoGame} alt="" />
            </div>
          }
          </div>
            <div className="gslot__caption">
              <b>{s.caption}</b><span>{(i + 1).toString().padStart(2, "0")} / {t.slots.length.toString().padStart(2, "0")}</span>
            </div>
          </div>
        )}
      </div>
    </section>);

}

/* -------------------- Resume / Ledger -------------------- */
const credits = [
{ year: "2026", title: "SKATE.", role: "Senior Environment Artist II - Procedural Technical Art Focus", studio: "Electronic Arts · Full Circle Studio" },
{ year: "2015-2021", title: "LiveLike VR Sports", role: "Senior Tech Artist", studio: "LiveLike VR" },
{ year: "2018", title: "Red Dead Redemption 2", role: "Senior Environment Artist - Terrain, Photogrammetry & PBR Materials Focus", studio: "Rockstar Games" },
{ year: "2013", title: "Grand Theft Auto V", role: "Senior 3D Environment Artist", studio: "Rockstar Games" },
{ year: "2010", title: "Red Dead Redemption", role: "Lighting Artist", studio: "Rockstar Games" },
{ year: "2008", title: "Midnight Club: Los Angeles", role: "3D Environment Artist", studio: "Rockstar Games" }];


function Resume() {
  return (
    <section id="resume" className="section" data-screen-label="Credits">
      <div className="section__head">
        <div className="section__index"><b>05</b> · Shipped Titles · Credits</div>
        <h2 className="section__title">
          <span className="tok">{`// shipped[]`}</span>Selected shipped titles and production experience.
        </h2>
      </div>
      <div className="ledger">
        {credits.map((c, i) =>
        <div className="ledger__row" key={i}>
            <div className="ledger__year">{c.year}</div>
            <div className="ledger__title">{c.title}</div>
            <div className="ledger__role">{c.role}</div>
            <div className="ledger__studio">{c.studio}</div>
            <div className="ledger__cta">View →</div>
          </div>
        )}
      </div>
    </section>);

}

/* -------------------- About -------------------- */
function About() {
  return (
    <section id="about" className="section" data-screen-label="About">
      <div className="section__head">
        <div className="section__index"><b>06</b> · About</div>
        <h2 className="section__title">
          <span className="tok">{`// readme.md`}</span>Environment art roots, technical art systems, procedural tools.
        </h2>
      </div>
      <div className="about">
        <div className="about__portrait">
          <img className="about__portrait__img" src="/media/profile.jfif" alt="Ruben Tavares" />
        </div>
        <div className="about__copy">
          <p>My background is AAA environment art: building worlds, reading production constraints, and understanding where procedural systems help or get in the way.</p>
          <p>At LiveLike I worked as a Senior Technical Artist, building real-time environment, shader, and pipeline solutions for XR and broadcast-style experiences. At EA, procedural building workflows became a practical focus: how to give artists speed without taking away authorship.</p>
          <p>My current work centers on procedural environment tools that feel direct, visual, and production-minded: Houdini HDAs, Python Viewer States, semantic attachment systems, and iteration loops that respect the artist in the viewport.</p>

          <div className="about__facts">
            <div>Years in industry <b>30+</b></div>
            <div>Shipped titles <b>5 AAA / XR</b></div>
            <div>Disciplines <b>Env Art · Tech Art</b></div>
            <div>Engines <b>Unity · Unreal · proprietary</b></div>
            <div>Languages <b>Python · C# · HLSL · VEX</b></div>
            <div>Tools <b>Houdini · Substance · Maya</b></div>
          </div>
        </div>
      </div>
    </section>);

}

/* -------------------- Contact -------------------- */
function Contact() {
  return (
    <section id="contact" className="contact" data-screen-label="Contact">
      <div className="contact__kicker">07 · Let's build something</div>
      <h2 className="contact__title">
        Currently open to senior<br />
        environment <em>&amp;</em> tech-art roles.
      </h2>
      <a className="contact__email" href="mailto:rubenltn@gmail.com">rubenltn@gmail.com</a>
      <div className="contact__sub">Reels & extended portfolio on request · NDA work available under signature</div>
      <div className="contact__links">
        <a href="https://www.linkedin.com/in/tavares-ruben/" target="_blank" rel="noreferrer">LinkedIn ↗</a>
        <a href="/media/Resume/Ruben_Tavares_Resume_STA_2026.pdf" target="_blank" rel="noreferrer">Résumé.pdf ↗</a>
      </div>
    </section>);

}

/* -------------------- Footer -------------------- */
function Footer() {
  return (
    <footer className="footer">
      <div>© 2026 · RUBEN TAVARES</div>
      <div className="footer__center">Site built by hand · No frameworks were harmed</div>
      <div className="footer__right">v3.2 · Last cooked May 2026</div>
    </footer>);

}

const BUILDING_MEDIA_BASE = "/media/Personal/technical_art/houdini/building_generator/";
const BUILDING_MEDIA = [
  { file: "bg_demo_video_final.mp4", title: "Hero demo", chip: "VIDEO", size: "hero" },
  { file: "bg_blockout_handles.gif", title: "Blockout Handles", chip: "BLOCKOUT", size: "third", description: "Viewport handles reshape the building massing directly while preserving procedural state." },
  { file: "bg_contextual_menu.gif", title: "Context Menu", chip: "UI", size: "third", description: "Selecting a face exposes context-aware actions for editing, styling, and component placement." },
  { file: "bg_face_extrusion.gif", title: "Face Extrusion", chip: "FACE OPS", size: "third", description: "Facade faces can be extruded into new procedural operations instead of destructive geometry edits." },
  { file: "bg_floor_duplication.gif", title: "Floor Duplication", chip: "FLOORS", size: "third", description: "Floor layouts can be duplicated upward to quickly build repeated architectural structure." },
  { file: "bg_multifloor_move.gif", title: "Multi-Floor Editing", chip: "FLOORS", size: "third", description: "Larger building regions can be adjusted across floors while maintaining semantic relationships." },
  { file: "bg_component_manipulation.gif", title: "Component Manipulation", chip: "COMPONENTS", size: "third", description: "Windows, doors, and columns can be moved, scaled, and pushed along their parent face normal." },
  { file: "bg_face_copypaste.gif", title: "Face Copy/Paste", chip: "FACE OPS", size: "third", description: "Entire facade layouts can be copied between faces and adapted to different wall dimensions." },
  { file: "bg_component_copypaste.gif", title: "Component Copy/Paste", chip: "COMPONENTS", size: "third", description: "Placed components can be reused while preserving their normalized face-space placement." },
  { file: "bg_style_options.gif", title: "Style Options", chip: "STYLE", size: "third", description: "Architecture styles can be changed through the tool while keeping component placement intact." },
  { file: "bg_work_modes.gif", title: "Work Modes", chip: "MODES", size: "third", description: "Fast, Precision, and Preview modes let the artist balance responsiveness and visual feedback." },
  { file: "bg_adaptive_mode.gif", title: "Adaptive Cache", chip: "CACHE", size: "third", description: "Adaptive caching can be toggled independently to keep interactive edits responsive." },
];

const ROAD_MEDIA = [
  {
    src: "/media/Personal/technical_art/houdini/road_generator/rg_creation.gif",
    title: "Curve Creation",
    chip: "CURVES",
    size: "half",
    description: "Center curves define the authored road network while procedural profile rules generate road, curb, sidewalk, and guide geometry.",
  },
  {
    src: "/media/Personal/technical_art/houdini/road_generator/rg_intersections.gif",
    title: "Intersection Solve",
    chip: "INTERSECTIONS",
    size: "half",
    description: "Intersection regions are resolved from the generated footprint so lane strips, sidewalks, and boundary curves can stay clean through edits.",
  },
];

function CaseMediaCard({ item, onOpen }) {
  const src = item.src || `${BUILDING_MEDIA_BASE}${item.file}`;
  const mediaName = item.file || item.src || "";
  const isVideo = item.kind === "video" || mediaName.endsWith(".mp4");
  const isGif = mediaName.endsWith(".gif");
  const media = isVideo ? (
    <video src={src} muted autoPlay loop playsInline preload="metadata" />
  ) : (
    <img src={src} alt={item.title} loading="lazy" />
  );

  return (
    <div className={`case-media case-media--${item.size}${isGif ? " case-media--zoomable" : ""}`}>
      <span className={`gslot__chip${isVideo ? " gslot__chip--accent" : ""}`}>{item.chip}</span>
      {isGif ? (
        <button
          type="button"
          className="case-media__frame case-media__frame--button"
          onClick={() => onOpen?.({ ...item, src })}
          aria-label={`Open ${item.title} preview`}
        >
          {media}
          <span className="case-media__zoom-hint">Click to enlarge</span>
        </button>
      ) : (
        <div className="case-media__frame">{media}</div>
      )}
      <div className="gslot__caption case-media__caption">
        <div className="case-media__caption-head">
          <b>{item.title}</b>
        </div>
        {item.description && <p>{item.description}</p>}
      </div>
    </div>
  );
}

function MediaLightbox({ item, onClose }) {
  useEffect(() => {
    if (!item) return undefined;

    const previousOverflow = document.body.style.overflow;
    const handleKeyDown = (event) => {
      if (event.key === "Escape") onClose();
    };

    document.body.style.overflow = "hidden";
    window.addEventListener("keydown", handleKeyDown);

    return () => {
      document.body.style.overflow = previousOverflow;
      window.removeEventListener("keydown", handleKeyDown);
    };
  }, [item, onClose]);

  if (!item) return null;

  return (
    <div className="media-lightbox" role="dialog" aria-modal="true" aria-label={`${item.title} enlarged preview`} onClick={onClose}>
      <div className="media-lightbox__panel" onClick={(event) => event.stopPropagation()}>
        <div className="media-lightbox__bar">
          <div>
            <span>{item.chip}</span>
            <b>{item.title}</b>
          </div>
          <button type="button" className="media-lightbox__close" onClick={onClose}>Close</button>
        </div>
        {item.description && <p className="media-lightbox__description">{item.description}</p>}
        <div className="media-lightbox__frame">
          <img src={item.src} alt={item.title} />
        </div>
      </div>
    </div>
  );
}

function FlowDiagram() {
  const nodes = ["ARTIST_INPUT", "PYTHON_VIEWER_STATE", "HDA_STATE", "PROCEDURAL_EVALUATION", "GENERATED_BUILDING", "CHILD_COMPONENT_HDAS"];
  return (
    <div className="diagram-panel">
      <div className="diagram-panel__header">
        <span>PROCEDURAL_BUILDING_TOOL / FLOW</span>
        <span>VIEWPORT-FIRST AUTHORING</span>
      </div>
      <div className="flow-diagram">
        {nodes.map((node, i) => (
          <React.Fragment key={node}>
            <div className={i === 1 || i === 2 ? "flow-node flow-node--accent" : "flow-node"}>{node}</div>
            {i < nodes.length - 1 && <div className="flow-arrow">-&gt;</div>}
          </React.Fragment>
        ))}
      </div>
    </div>
  );
}

function RoadFlowDiagram() {
  const rows = [
    ["CENTER CURVES", "PROFILE WIDTH", "2D ASPHALT FOOTPRINT", "BOOLEAN / UNION"],
    ["INTERSECTION MASKS", "BOUNDARY CURVES", "CURB / SIDEWALK SWEEPS", "LANE STRIPS"],
  ];

  return (
    <div className="diagram-panel">
      <div className="diagram-panel__header">
        <span>PROCEDURAL_ROAD_GENERATOR / FLOW</span>
        <span>PROFILE-DRIVEN FOOTPRINTS</span>
      </div>
      <div className="road-flow-diagram">
        {rows.map((row, rowIndex) => (
          <div className="road-flow-row" key={rowIndex}>
            {row.map((node, nodeIndex) => {
              const isAccent = node === "2D ASPHALT FOOTPRINT" || node === "BOUNDARY CURVES";
              return (
                <React.Fragment key={node}>
                  <div className={isAccent ? "road-flow-node road-flow-node--accent" : "road-flow-node"}>{node}</div>
                  {nodeIndex < row.length - 1 && <div className="road-flow-arrow">-&gt;</div>}
                </React.Fragment>
              );
            })}
          </div>
        ))}
      </div>
    </div>
  );
}

function DataDiagram() {
  const nodes = [
    { label: "Building", meta: "bg_blockout_build" },
    { label: "Floors", meta: "floor_ops / floor bottoms" },
    { label: "Rows", meta: "row_ops / style bands" },
    { label: "Columns", meta: "column_ops / vertical groups" },
    { label: "Faces", meta: "face_ops / edge_ops_json" },
    { label: "Components", meta: "face_id + floor_id + u/v/depth" },
  ];
  return (
    <div className="diagram-panel diagram-panel--data">
      <div className="diagram-panel__header">
        <span>STATE / HIERARCHY</span>
        <span>SEMANTIC ATTACHMENT</span>
      </div>
      <div className="data-tree">
        {nodes.map((node, i) => (
          <div className="data-tree__row" key={node.label} style={{ "--level": i }}>
            <div className="data-tree__node">
              <b>{node.label}</b>
              <span>{node.meta}</span>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

function CodeCard() {
  const code = `component_attachment = {
  "face_id": face_id,
  "floor_id": floor_id,
  "u": normalized_x,
  "v": normalized_y,
  "depth": face_normal_offset,
}`;
  return (
    <div className="code-card">
      <div className="diagram-panel__header">
        <span>ATTACHMENT PAYLOAD</span>
        <span>PYTHON / JSON STATE</span>
      </div>
      <pre><code>{code}</code></pre>
    </div>
  );
}

function CaseAside({ item }) {
  return (
    <aside className="case__aside">
      <div>Project</div>
      <p>{item.project}</p>
      <h4>Role</h4>
      <p>{item.role}</p>
      <h4>Context</h4>
      <p>{item.context}</p>
      <h4>Stack</h4>
      <ul>
        {item.stack.map((entry) => <li key={entry}>{entry}</li>)}
      </ul>
      <h4>Focus</h4>
      <ul>
        {item.focus.map((entry) => <li key={entry}>{entry}</li>)}
      </ul>
      {item.tags && (
        <>
          <h4>Tags</h4>
          <ul>
            {item.tags.map((entry) => <li key={entry}>{entry}</li>)}
          </ul>
        </>
      )}
    </aside>
  );
}

function PlaceholderCaseMedia({ title, index }) {
  return (
    <div className="case-media case-media--half">
      <span className="gslot__chip">IMAGE</span>
      <div className="case-media__frame">
        <div className="placeholder"><span>{title}</span></div>
      </div>
      <div className="gslot__caption case-media__caption">
        <div className="case-media__caption-head">
          <b>{title}</b>
        </div>
        <p>Placeholder frame {String(index + 1).padStart(2, "0")} for the upcoming media pass.</p>
      </div>
    </div>
  );
}

function PlaceholderCaseBody({ item }) {
  return (
    <div className="case__body">
      <div className="case__intro">
        <p>{item.intro}</p>
      </div>

      <div>
        <h3><span className="num">01</span>Overview</h3>
        <p>This section is scaffolded to match the completed case-study structure. The final copy can drop into the same rhythm: overview, problem, tool design, architecture, interaction media, and lessons learned.</p>
      </div>

      <div className="case__media-grid case__media-grid--reels">
        {item.placeholders.map((title, index) => <PlaceholderCaseMedia key={title} title={title} index={index} />)}
      </div>

      <div>
        <h3><span className="num">02</span>The System</h3>
        <p>Placeholder text for the system design. This will describe the authoring model, where the procedural state lives, and how artists interact with the tool.</p>
      </div>

      <div className="case__split">
        <div className="diagram-panel">
          <div className="diagram-panel__header">
            <span>WORKFLOW / PLACEHOLDER</span>
            <span>{item.project}</span>
          </div>
          <div className="data-tree">
            {["Input", "Tool State", "Procedural Solve", "Output"].map((node, index) => (
              <div className="data-tree__row" key={node} style={{ "--level": index }}>
                <div className="data-tree__node">
                  <b>{node}</b>
                  <span>content pending</span>
                </div>
              </div>
            ))}
          </div>
        </div>
        <div className="code-card">
          <div className="diagram-panel__header">
            <span>NOTES</span>
            <span>CONTENT PASS</span>
          </div>
          <pre><code>{`case_study = {
  "status": "placeholder",
  "media": "pending",
  "copy": "pending",
}`}</code></pre>
        </div>
      </div>

      <div>
        <h3><span className="num">03</span>Next Content Pass</h3>
        <p>Once the final material is ready, this tab can be filled with real media, specific workflow notes, and the same production-minded tone used in the Building Tool case study.</p>
      </div>
    </div>
  );
}

function RoadGeneratorBody({ item, onOpen }) {
  return (
    <div className="case__body">
      <div className="case__intro">
        <p>{item.headline}</p>
        <p>{item.subtitle}</p>
      </div>

      <CaseMediaCard
        item={{
          kind: "still",
          src: item.hero,
          title: "Procedural road generator overview",
          chip: "IMAGE",
          size: "hero",
          description: "Profile-driven road footprints, intersections, sidewalk/curb boundaries, and lane-strip guide curves authored from center curves.",
        }}
      />

      <div>
        <h3><span className="num">01</span>The Problem</h3>
        <p>Road tools can become brittle when the road surface, intersections, sidewalks, curbs, and markings are treated as one swept mesh. The result is hard to clean, hard to shade, and hard to debug once multiple roads overlap.</p>
        <p>This tool keeps those systems related, but separate enough that each layer can be solved, inspected, and cleaned with its own rules.</p>
      </div>

      <div>
        <h3><span className="num">02</span>The Approach</h3>
        <p>{item.intro}</p>
        <ul className="case__bullets">
          <li>Asphalt is solved as polygon footprint geometry</li>
          <li>Curbs and sidewalks are generated from cleaned boundary curves</li>
          <li>Lane markings are independent guide/decal curves</li>
          <li>Intersections are masks and custom logic</li>
          <li>Attributes act as the contract between generation, materials, debugging, and cleanup</li>
        </ul>
      </div>

      <RoadFlowDiagram />

      <div>
        <h3><span className="num">03</span>Interaction Reels</h3>
        <p>These clips show the authored curve workflow and the procedural intersection response: the artist edits path input, while the generator rebuilds road footprints, lane guides, and cleaned boundaries from the same network state.</p>
      </div>

      <div className="case__media-grid case__media-grid--reels">
        {ROAD_MEDIA.map((media) => <CaseMediaCard key={media.src} item={media} onOpen={onOpen} />)}
      </div>

      <div>
        <h3><span className="num">04</span>Profile System</h3>
        <p>Road width is derived from lane count, lane width, two-way traffic, bike lanes, gutters, curbs, sidewalks, lane dividers, and center divider widths.</p>
        <p>Profile segments are tagged with attributes such as sidewalk, curb, gutter, pavement, bike_lane, travel_lane, lane_divider, and center_divider so downstream systems can reason about what each generated strip represents.</p>
      </div>

      <div>
        <h3><span className="num">05</span>Intersection Strategy</h3>
        <p>Intersections are solved in 2D plan view rather than by overlapping full swept road meshes. The asphalt footprint is unioned first, then masks and custom logic handle intersection regions before boundary curves are extracted.</p>
      </div>

      <div>
        <h3><span className="num">06</span>Boundary Cleanup</h3>
        <p>Boundary curves are cleaned with Fuse, Polypath, colinear point removal, graph-based neighbor logic, and corner rounding. Curb sweep orientation is stabilized using outward normal solving against asphalt reference geometry.</p>
        <p>Curve rebuilds preserve vertex UVs, and small isolated geometry fragments are removed through connectivity-based cleanup.</p>
      </div>

      <div>
        <h3><span className="num">07</span>What I Learned</h3>
        <p>The useful abstraction is not the center curve by itself. It is the contract between center curves, profile attributes, footprint geometry, boundary cleanup, and sweep-ready outputs.</p>
        <p>Keeping those layers explicit makes the tool easier to debug and gives production artists cleaner handoff points for materials, decals, sidewalks, curbs, and other city-layout systems.</p>
      </div>

      <div>
        <h3><span className="num">08</span>Future Plans</h3>
        <p>The current road generator focuses on the foundation: profile-driven road widths, intersection footprints, curb and sidewalk boundaries, lane guide curves, cleanup utilities, and material-friendly attributes.</p>
        <p>The next pass would move the tool from procedural road construction toward a more complete city-layout authoring system. Planned work includes intersection dressing, traffic-light placement, road markings, sidewalk props, and custom Viewer State controls for editing lanes, sidewalks, and intersections directly in the viewport.</p>
        <div className="future-grid">
          {item.futurePlans.map((plan, index) => (
            <div className="future-card" key={plan.title}>
              <span>{String(index + 1).padStart(2, "0")}</span>
              <b>{plan.title}</b>
              <p>{plan.text}</p>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

function RampRoadTerrainBody({ item }) {
  return (
    <div className="case__body">
      <div className="case__intro">
        <p>{item.subtitle}</p>
      </div>

      <div>
        <h3><span className="num">01</span>Overview</h3>
        <p>{item.intro}</p>
        <p>{item.description}</p>
      </div>

      <CaseMediaCard
        item={{
          kind: "video",
          src: item.video,
          title: "Unity + Houdini Engine path authoring",
          chip: "VIDEO",
          size: "hero",
          description: "Curve-driven path editing in Unity with Houdini Engine generating connected procedural path, ramp, and terrain responses.",
        }}
      />

      <div>
        <h3><span className="num">02</span>System Breakdown</h3>
        <ol className="case__numbered">
          {item.breakdown.map((step) => (
            <li key={step.num}>
              <span>{step.num}</span>
              <div>
                <b>{step.title}</b>
                <p>{step.text}</p>
              </div>
            </li>
          ))}
        </ol>
      </div>

      <div>
        <h3><span className="num">03</span>Reusable Attachment Pattern</h3>
        <p>The important part is the relationship between the Unity-authored source curve and the generated Houdini assets. The path remains the source of truth, while attached tools read its output and solve their own procedural result from that relationship.</p>
        <p>That makes the setup useful beyond the quarter-pipe example: the same pattern can drive curbs, rails, barriers, ledges, sidewalks, and other border assets that need to stay editable as gameplay layout changes.</p>
      </div>
    </div>
  );
}

function CaseStudy() {
  const [lightboxItem, setLightboxItem] = useState(null);
  const [activeId, setActiveId] = useState(TECH_ART_CASES[0].id);
  const activeCase = TECH_ART_CASES.find((item) => item.id === activeId) || TECH_ART_CASES[0];
  const isBuildingTool = activeCase.id === "building-tool";
  const isRoadSystem = activeCase.id === "road-system";
  const isRampRoadTerrain = activeCase.id === "ramp-road-terrain";

  useEffect(() => {
    function handleSelect(event) {
      const nextId = event.detail?.id;
      if (TECH_ART_CASES.some((item) => item.id === nextId)) setActiveId(nextId);
    }

    window.addEventListener("tech-art-select", handleSelect);
    return () => window.removeEventListener("tech-art-select", handleSelect);
  }, []);

  return (
    <section id="case-studies" className="section" data-screen-label="Case Studies">
      <div className="section__head">
        <div className="section__index"><b>02</b> · CASE STUDIES</div>
        <h2 className="section__title">
          <span className="tok">{`// tech_art/procedural_tools`}</span>Technical art and procedural tools for real-time production.
        </h2>
        <div className="section__subtitle">Viewport-first Houdini, Unity, shader, and procedural environment systems</div>
      </div>

      <div className="gallery__bar case-tabs" role="tablist" aria-label="Case studies">
        <span className="gallery__bar__label">Case Studies /</span>
        {TECH_ART_CASES.map((item) => (
          <button
            key={item.id}
            type="button"
            className="gallery__tab case-tab"
            aria-pressed={item.id === activeCase.id}
            role="tab"
            aria-selected={item.id === activeCase.id}
            onClick={() => setActiveId(item.id)}
          >
            {item.tab}<span className="yr">{item.num}</span>
          </button>
        ))}
      </div>

      <div className="case">
        <CaseAside item={activeCase} />

        {isBuildingTool ? (
          <div className="case__body">
          <div className="case__intro">
            <p>A Houdini HDA with a custom Python Viewer State for direct viewport editing, semantic component attachment, and production-minded facade workflows.</p>
          </div>

          <div>
            <h3><span className="num">01</span>Overview</h3>
            <p>This is not a generic building generator. It is a direct-manipulation procedural authoring tool.</p>
            <p>The main achievement is turning a Houdini HDA into a tactile viewport-first design tool: artists edit the building in context, while the procedural system keeps enough structure to rebuild cleanly.</p>
          </div>

          <CaseMediaCard item={BUILDING_MEDIA[0]} onOpen={setLightboxItem} />

          <div>
            <h3><span className="num">02</span>The Problem</h3>
            <p>Most procedural building tools are fast until an artist needs to make a specific choice. Then the workflow drops into parameter hunting, manual overrides, or destructive edits that break the procedural model.</p>
            <p>This tool explores a stricter idea: keep the building live, but let the artist touch it directly in the viewport.</p>
          </div>

          <blockquote className="case__pullquote">
            This is not a generic building generator. It is a direct-manipulation procedural authoring tool.
            <span>the design constraint behind the system</span>
          </blockquote>

          <div>
            <h3><span className="num">03</span>The Tool</h3>
            <p>The core achievement is turning a Houdini HDA into a tactile viewport-first design tool. Handles, menus, face operations, floor duplication, style options, and component placement are authored through artist gestures rather than exposed as a wall of parameters.</p>
            <p>The Viewer State translates those gestures into persistent procedural state. The HDA evaluates that state and regenerates the building without losing the authored intent.</p>
          </div>

          <div id="tech">
            <h3><span className="num">04</span>The Architecture</h3>
            <p>The Python Viewer State owns interaction. The HDA owns evaluation. JSON-backed state sits between them so edits can be serialized, replayed, copied, and kept stable as the generated form changes.</p>
          </div>

          <FlowDiagram />

          <div className="architecture-copy">
            <h3>The tool separates artist gestures from procedural state.</h3>
            <p>The viewer state acts as the front end of the tool. It listens to handles, face picks, menu actions, and component gestures, then writes compact state back onto the HDA for procedural evaluation.</p>
          </div>
          <div className="tech__stack">
            <div><span>Interaction</span><span>Python Viewer State</span></div>
            <div><span>Storage</span><span>JSON state</span></div>
            <div><span>Components</span><span>Child HDAs</span></div>
            <div><span>Evaluation</span><span>Houdini HDA</span></div>
            <div><span>Geometry</span><span>VEX / SOPs</span></div>
            <div><span>Validation</span><span>Unreal planned</span></div>
          </div>

          <div className="graph">
            <div className="graph__header">
              <span>PROCEDURAL_BUILDING_TOOL · /obj/building_authoring</span>
              <span>stateful viewport editing</span>
            </div>
            <svg className="graph__svg graph__svg--diagram" viewBox="0 0 930 520" preserveAspectRatio="xMidYMid meet" aria-label="Procedural building tool architecture graph">
              <defs>
                <marker id="building-arrow-case" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
                  <path d="M0,0 L10,5 L0,10 z" fill="currentColor" />
                </marker>
              </defs>
              <g className="graph-wires">
                <path d="M 205 110 C 260 110, 250 220, 315 220" />
                <path d="M 205 245 C 255 245, 260 245, 315 245" />
                <path className="wire-accent" d="M 205 380 C 260 380, 250 270, 315 270" />
                <path d="M 485 245 C 525 245, 525 245, 565 245" />
                <path d="M 705 245 C 745 245, 745 190, 785 190" />
                <path className="wire-accent" d="M 705 245 C 745 245, 745 335, 785 335" />
              </g>

              <g className="svg-node" transform="translate(45 74)">
                <rect className="svg-node__box" width="160" height="72" />
                <rect className="svg-node__head" width="160" height="24" />
                <text className="svg-node__title" x="10" y="16">ARTIST_INPUT</text>
                <rect className="svg-node__badge" x="120" y="6" width="30" height="12" />
                <text className="svg-node__badge-text" x="135" y="15">VIEW</text>
                <text className="svg-node__body" x="10" y="43"><tspan className="strong">handles</tspan><tspan> / picks</tspan></text>
                <text className="svg-node__body" x="10" y="62"><tspan className="strong">menus</tspan><tspan> / gestures</tspan></text>
              </g>

              <g className="svg-node" transform="translate(45 209)">
                <rect className="svg-node__box" width="160" height="72" />
                <rect className="svg-node__head" width="160" height="24" />
                <text className="svg-node__title" x="10" y="16">FACE/FLOOR_OPS</text>
                <rect className="svg-node__badge" x="118" y="6" width="32" height="12" />
                <text className="svg-node__badge-text" x="134" y="15">STATE</text>
                <text className="svg-node__body" x="10" y="43"><tspan className="strong">split</tspan><tspan> / extrude</tspan></text>
                <text className="svg-node__body" x="10" y="62"><tspan className="strong">rows</tspan><tspan> / columns</tspan></text>
              </g>

              <g className="svg-node" transform="translate(45 344)">
                <rect className="svg-node__box" width="160" height="72" />
                <rect className="svg-node__head" width="160" height="24" />
                <text className="svg-node__title" x="10" y="16">COMPONENTS</text>
                <rect className="svg-node__badge" x="124" y="6" width="26" height="12" />
                <text className="svg-node__badge-text" x="137" y="15">HDA</text>
                <text className="svg-node__body" x="10" y="43"><tspan className="strong">attach</tspan><tspan> by face</tspan></text>
                <text className="svg-node__body" x="10" y="62"><tspan className="strong">paste</tspan><tspan> relative</tspan></text>
              </g>

              <g className="svg-node svg-node--accent" transform="translate(315 196)">
                <rect className="svg-node__box" width="170" height="98" />
                <rect className="svg-node__head" width="170" height="24" />
                <text className="svg-node__title" x="10" y="16">PY_VIEWER_STATE</text>
                <rect className="svg-node__badge" x="138" y="6" width="22" height="12" />
                <text className="svg-node__badge-text" x="149" y="15">PY</text>
                <text className="svg-node__body" x="10" y="45"><tspan className="strong">writes</tspan><tspan> HDA state</tspan></text>
                <text className="svg-node__body" x="10" y="64"><tspan className="strong">guards</tspan><tspan> recooks</tspan></text>
                <text className="svg-node__body" x="10" y="83"><tspan className="strong">routes</tspan><tspan> events</tspan></text>
              </g>

              <g className="svg-node svg-node--accent" transform="translate(565 196)">
                <rect className="svg-node__box" width="140" height="98" />
                <rect className="svg-node__head" width="140" height="24" />
                <text className="svg-node__title" x="10" y="16">HDA_STATE</text>
                <rect className="svg-node__badge" x="102" y="6" width="28" height="12" />
                <text className="svg-node__badge-text" x="116" y="15">JSON</text>
                <text className="svg-node__body" x="10" y="45"><tspan className="strong">floor_ops</tspan></text>
                <text className="svg-node__body" x="10" y="64"><tspan className="strong">face_ops</tspan></text>
                <text className="svg-node__body" x="10" y="83"><tspan>component_refs</tspan></text>
              </g>

              <g className="svg-node" transform="translate(785 142)">
                <rect className="svg-node__box" width="120" height="96" />
                <rect className="svg-node__head" width="120" height="24" />
                <text className="svg-node__title" x="10" y="16">EVALUATION</text>
                <rect className="svg-node__badge" x="84" y="6" width="26" height="12" />
                <text className="svg-node__badge-text" x="97" y="15">SOP</text>
                <text className="svg-node__body" x="10" y="45"><tspan className="strong">PythonModule</tspan></text>
                <text className="svg-node__body" x="10" y="64"><tspan>attachment solve</tspan></text>
                <text className="svg-node__body" x="10" y="83"><tspan>VEX / SOPs</tspan></text>
              </g>

              <g className="svg-node svg-node--accent" transform="translate(785 304)">
                <rect className="svg-node__box" width="120" height="86" />
                <rect className="svg-node__head" width="120" height="24" />
                <text className="svg-node__title" x="10" y="16">OUTPUT</text>
                <rect className="svg-node__badge" x="84" y="6" width="26" height="12" />
                <text className="svg-node__badge-text" x="97" y="15">GEO</text>
                <text className="svg-node__body" x="10" y="45"><tspan className="strong">building</tspan><tspan> mesh</tspan></text>
                <text className="svg-node__body" x="10" y="64"><tspan className="strong">child</tspan><tspan> HDAs</tspan></text>
              </g>
            </svg>
          </div>

          <div>
            <h3><span className="num">05</span>The Core Idea</h3>
            <p>Components are not stored as raw world-space transforms. They are attached to generated faces using face_id, floor_id, normalized u/v coordinates, and depth offsets.</p>
            <p>That lets a window, sign, trim piece, or facade detail stay live through building edits. It can also be copied and pasted relatively across different face sizes instead of becoming a one-off manual placement.</p>
          </div>

          <div className="case__split">
            <DataDiagram />
            <CodeCard />
          </div>

          <div>
            <h3><span className="num">06</span>Interaction Reels</h3>
            <p>The clips below show the authoring surface: blockout handles, contextual edits, face and floor operations, component manipulation, copy/paste behavior, style choices, work modes, and adaptive evaluation.</p>
          </div>

          <div className="case__media-grid case__media-grid--reels">
            {BUILDING_MEDIA.slice(1).map((item) => <CaseMediaCard key={item.file} item={item} onOpen={setLightboxItem} />)}
          </div>

          <div>
            <h3><span className="num">07</span>Performance Iteration</h3>
            <p>The tool is built around interaction first, so performance work focuses on keeping edits responsive while preserving enough procedural context to evaluate accurately.</p>
            <ul className="case__bullets">
              <li>Batching component creation</li>
              <li>Batching merge connections</li>
              <li>Deferring intersector rebuilds</li>
              <li>Separating Fast mode from Adaptive Cache</li>
              <li>Navigation guards to avoid recooking during camera movement</li>
              <li>JSON-backed hot references like component_refs_json and edge_ops_json</li>
            </ul>
          </div>

          <div>
            <h3><span className="num">08</span>What I Learned</h3>
            <p>The useful line is not procedural versus handmade. It is whether the system preserves artistic decisions when the design changes.</p>
            <p>A procedural building tool becomes production-worthy when it can remember intent: which face mattered, which floor it belonged to, where the component sat in normalized space, and how to rebuild without erasing the hand of the artist.</p>
          </div>
        </div>
        ) : isRoadSystem ? (
          <RoadGeneratorBody item={activeCase} onOpen={setLightboxItem} />
        ) : isRampRoadTerrain ? (
          <RampRoadTerrainBody item={activeCase} />
        ) : (
          <PlaceholderCaseBody item={activeCase} />
        )}
      </div>
      <MediaLightbox item={lightboxItem} onClose={() => setLightboxItem(null)} />
    </section>);

}

function TechArt() {
  return (
    <section id="tech" className="section" data-screen-label="Architecture">
      <div className="section__head">
        <div className="section__index"><b>04</b> · THE ARCHITECTURE</div>
        <h2 className="section__title">
          <span className="tok">{`// architecture`}</span>The tool separates artist gestures from procedural state.
        </h2>
      </div>
      <div className="case architecture-case">
        <aside className="case__aside">
          <div>Project</div>
          <p>Procedural Building Authoring Tool</p>
          <h4>Role</h4>
          <p>Technical Artist / Tool Designer / Procedural Systems</p>
          <h4>Stack</h4>
          <ul>
            <li>Houdini HDA</li>
            <li>Python Viewer State</li>
            <li>PythonModule</li>
            <li>VEX / SOPs</li>
            <li>JSON state</li>
          </ul>
          <h4>Focus</h4>
          <p>Stateful viewport editing</p>
          <p>Semantic component attachment</p>
          <p>Performance batching</p>
        </aside>

        <div className="case__body architecture-body">
          <div className="architecture-copy">
            <h3>The tool separates artist gestures from procedural state.</h3>
            <p>The viewer state acts as the front end of the tool. It listens to handles, face picks, menu actions, and component gestures, then writes compact state back onto the HDA for procedural evaluation.</p>
          </div>
          <div className="tech__stack">
            <div><span>Interaction</span><span>Python Viewer State</span></div>
            <div><span>Storage</span><span>JSON state</span></div>
            <div><span>Components</span><span>Child HDAs</span></div>
            <div><span>Evaluation</span><span>Houdini HDA</span></div>
            <div><span>Geometry</span><span>VEX / SOPs</span></div>
            <div><span>Validation</span><span>Unreal planned</span></div>
          </div>

          <div className="graph">
            <div className="graph__header">
              <span>PROCEDURAL_BUILDING_TOOL · /obj/building_authoring</span>
              <span>stateful viewport editing</span>
            </div>
            <svg className="graph__svg" viewBox="0 0 700 500" preserveAspectRatio="none">
              <defs>
                <marker id="building-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
                  <path d="M0,0 L10,5 L0,10 z" fill="var(--fg)" />
                </marker>
              </defs>
              <path d="M 126 115 C 220 115, 230 215, 305 215" stroke="var(--fg)" strokeWidth="1.5" fill="none" markerEnd="url(#building-arrow)" />
              <path d="M 126 255 C 220 255, 230 235, 305 235" stroke="var(--fg)" strokeWidth="1.5" fill="none" markerEnd="url(#building-arrow)" />
              <path d="M 126 385 C 220 385, 230 255, 305 255" stroke="var(--accent)" strokeWidth="1.5" fill="none" markerEnd="url(#building-arrow)" />
              <path d="M 382 225 C 485 225, 505 155, 586 155" stroke="var(--fg)" strokeWidth="1.5" fill="none" markerEnd="url(#building-arrow)" />
              <path d="M 382 250 C 485 250, 505 335, 586 335" stroke="var(--accent)" strokeWidth="1.5" fill="none" markerEnd="url(#building-arrow)" />
            </svg>

            <div className="node" style={{ top: '12%', left: '5%' }}>
              <div className="node__title"><b>ARTIST_INPUT</b><span>VIEW</span></div>
              <div className="node__body">
                <span><b>handles</b> / picks</span>
                <span><b>menus</b> / gestures</span>
              </div>
            </div>
            <div className="node" style={{ top: '40%', left: '5%' }}>
              <div className="node__title"><b>FACE_OPS</b><span>STATE</span></div>
              <div className="node__body">
                <span><b>extrude</b> / copy</span>
                <span><b>floor</b> edits</span>
              </div>
            </div>
            <div className="node" style={{ top: '66%', left: '5%' }}>
              <div className="node__title"><b>COMPONENTS</b><span>HDA</span></div>
              <div className="node__body">
                <span><b>attach</b> by face</span>
                <span><b>paste</b> relative</span>
              </div>
            </div>

            <div className="node node--accent" style={{ top: '39%', left: '42%' }}>
              <div className="node__title"><b>PY_VIEWER_STATE</b><span>PY</span></div>
              <div className="node__body">
                <span><b>writes</b> JSON refs</span>
                <span><b>guards</b> recooks</span>
                <span><b>routes</b> HDA events</span>
              </div>
            </div>

            <div className="node" style={{ top: '26%', right: '4%' }}>
              <div className="node__title"><b>HDA_EVALUATION</b><span>SOP</span></div>
              <div className="node__body">
                <span><b>facades</b> / floors</span>
                <span><b>rows</b> / columns</span>
              </div>
            </div>
            <div className="node node--accent" style={{ top: '62%', right: '4%' }}>
              <div className="node__title"><b>BUILDING_OUTPUT</b><span>GEO</span></div>
              <div className="node__body">
                <span><b>building</b> mesh</span>
                <span><b>child</b> HDAs</span>
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>);

}

Object.assign(window, { TopBar, Hero, Marquee, Works, CaseStudy, TechArt, ShippedTitles, Resume, About, Contact, Footer });


