/* global React */
// Shared data + chrome for the Benjamin Partridge portfolio prototype.
// All content sourced from Ben's real portfolio (professional, personal, academic).
// Design system: assets/system.css ("Drawing Sheet").

const R = (typeof window !== 'undefined' && window.__resources) || {};
const px = (k, path) => R[k] || path;
const PIMG = {
  // professional heroes
  res:  px('res',  'assets/img/projects/stthomas/hero.webp'),
  life: px('life', 'assets/img/projects/lifetime/exterior-spa.webp'),
  olg:  px('olg',  'assets/img/projects/olg/entry.webp'),
  topo: px('topo', 'assets/img/topo.png'),
  portrait: px('portrait', 'assets/img/ben-portrait.png'),
  // our lady of grace gallery
  olgCastStone: px('olgCastStone', 'assets/img/projects/olg/caststone.webp'),
  olgMillwork:  px('olgMillwork',  'assets/img/projects/olg/millwork.webp'),
  olgGym:       px('olgGym',       'assets/img/projects/olg/gym.webp'),
  olgCollab:    px('olgCollab',    'assets/img/projects/olg/collab.webp'),
  olgMaker:     px('olgMaker',     'assets/img/projects/olg/maker.webp'),
  olgCommons:   px('olgCommons',   'assets/img/projects/olg/commons.webp'),
  olgConcourse: px('olgConcourse', 'assets/img/projects/olg/concourse.webp'),
  // life time gallery
  ltExtFront:  px('ltExtFront',  'assets/img/projects/lifetime/exterior-front.webp'),
  ltPool:      px('ltPool',      'assets/img/projects/lifetime/pool.webp'),
  ltExtSpa:    px('ltExtSpa',    'assets/img/projects/lifetime/exterior-spa.webp'),
  ltPickle:    px('ltPickle',    'assets/img/projects/lifetime/pickleball.webp'),
  ltRecovery:  px('ltRecovery',  'assets/img/projects/lifetime/recovery.webp'),
  ltWork:      px('ltWork',      'assets/img/projects/lifetime/ltwork.webp'),
  ltReception: px('ltReception', 'assets/img/projects/lifetime/reception.webp'),
  ltLounge:    px('ltLounge',    'assets/img/projects/lifetime/lounge.webp'),
  ltCafe:      px('ltCafe',      'assets/img/projects/lifetime/lifecafe.webp'),
  // professional gallery
  stReception: px('stReception', 'assets/img/projects/stthomas/reception.webp'),
  stLounge:    px('stLounge',    'assets/img/projects/stthomas/lounge.webp'),
  stEntrance:  px('stEntrance',  'assets/img/projects/stthomas/entrance.webp'),
  stAerial:    px('stAerial',    'assets/img/projects/stthomas/aerial.webp'),
  stStair:     px('stStair',     'assets/img/projects/stthomas/stair.webp'),
  stStudy:     px('stStudy',     'assets/img/projects/stthomas/study.webp'),
  stDining:    px('stDining',    'assets/img/projects/stthomas/dining.webp'),
  // personal before/after + galleries
  dorseyBefore: px('dorseyBefore', 'assets/img/projects/dorsey-before.webp'),
  dorseyAfter:  px('dorseyAfter',  'assets/img/projects/dorsey-after.webp'),
  montrealBefore: px('montrealBefore', 'assets/img/projects/montreal-k1-before.webp'),
  montrealAfter:  px('montrealAfter',  'assets/img/projects/montreal-k1-after.webp'),
  montrealBefore2: px('montrealBefore2', 'assets/img/projects/montreal-k2-before.webp'),
  montrealAfter2:  px('montrealAfter2',  'assets/img/projects/montreal-k2-after.webp'),
  hillparkHero:   px('hillparkHero',   'assets/img/projects/hillpark-hero.jpg'),
  hillparkVanity: px('hillparkVanity', 'assets/img/projects/hillpark-vanity.png'),
  hillparkShower: px('hillparkShower', 'assets/img/projects/hillpark-shower.png'),
  hillparkRender1: px('hillparkRender1', 'assets/img/projects/hillpark-rendering-1.jpg'),
  hillparkRender2: px('hillparkRender2', 'assets/img/projects/hillpark-rendering-2.jpg'),
  hillR1Section: px('hillR1Section', 'assets/img/projects/hillpark-r1-section.jpg'),
  hillR1Elev:    px('hillR1Elev',    'assets/img/projects/hillpark-r1-elevation.jpg'),
  hillR2Section: px('hillR2Section', 'assets/img/projects/hillpark-r2-section.jpg'),
  hillR2Elev:    px('hillR2Elev',    'assets/img/projects/hillpark-r2-elevation.jpg'),
  hillBefore1: px('hillBefore1', 'assets/img/projects/hillpark-before-1.webp'),
  hillAfter1:  px('hillAfter1',  'assets/img/projects/hillpark-after-1.webp'),
  hillBefore2: px('hillBefore2', 'assets/img/projects/hillpark-before-2.webp'),
  hillAfter2:  px('hillAfter2',  'assets/img/projects/hillpark-after-2.webp'),
  woodStreet:   px('woodStreet',   'assets/img/projects/wood-street.png'),
  belmontAve:   px('belmontAve',   'assets/img/projects/belmont-avenue.png'),
  // academic
  thesis:     px('thesis',     'assets/img/projects/thesis.png'),
  recurve:    px('recurve',    'assets/img/projects/recurve.png'),
  peritoneum: px('peritoneum', 'assets/img/projects/peritoneum.png'),
  heavyAir:   px('heavyAir',   'assets/img/projects/heavy-air.png'),
  wetland:    px('wetland',    'assets/img/projects/wetland.png'),
};

// Real contact details.
const CONTACT = {
  email: 'bencpartridge@gmail.com',
  phone: '480.323.5634',
  phoneHref: 'tel:+14803235634',
  locations: 'Minnesota & Arizona',
};

// Survey-contour motif — the cross-medium signature tying site to printed resume.
function TopoField({ tone = 'paper', opacity, crop = 'right', fade = true, style = {} }) {
  const dark = tone === 'dark';
  const op = opacity != null ? opacity : (dark ? 0.22 : 0.5);
  const pos = { right: 'right center', left: 'left center', top: 'center top', center: 'center' }[crop] || 'center';
  const mask = fade ? 'linear-gradient(90deg, transparent 0%, #000 22%, #000 100%)' : 'none';
  return (
    <div aria-hidden="true" style={{
      position: 'absolute', inset: 0, zIndex: 0, pointerEvents: 'none',
      backgroundImage: `url(${PIMG.topo})`, backgroundSize: 'cover', backgroundPosition: pos,
      backgroundRepeat: 'no-repeat', opacity: op,
      filter: dark ? 'brightness(2.4)' : 'none',
      WebkitMaskImage: mask, maskImage: mask, ...style,
    }}></div>
  );
}

// ── Before / After comparison slider ───────────────────────────
// Drag the handle (or click anywhere) to wipe between the two states.
function BeforeAfter({ before, after, beforeLabel = 'Before', afterLabel = 'After', height = 'clamp(320px,52vw,640px)' }) {
  const [pos, setPos] = React.useState(50);
  const [drag, setDrag] = React.useState(false);
  const ref = React.useRef(null);
  const move = React.useCallback((clientX) => {
    const el = ref.current; if (!el) return;
    const r = el.getBoundingClientRect();
    const pct = ((clientX - r.left) / r.width) * 100;
    setPos(Math.max(0, Math.min(100, pct)));
  }, []);
  React.useEffect(() => {
    if (!drag) return;
    const mm = (e) => move(e.touches ? e.touches[0].clientX : e.clientX);
    const up = () => setDrag(false);
    window.addEventListener('mousemove', mm); window.addEventListener('mouseup', up);
    window.addEventListener('touchmove', mm, { passive: false }); window.addEventListener('touchend', up);
    return () => {
      window.removeEventListener('mousemove', mm); window.removeEventListener('mouseup', up);
      window.removeEventListener('touchmove', mm); window.removeEventListener('touchend', up);
    };
  }, [drag, move]);
  const tag = (txt, side) => (
    <span style={{
      position:'absolute', top:'16px', [side]:'16px', zIndex:4,
      ...pmono({ fontSize:'10px' }), color:'#fff',
      background:'rgba(8,7,5,0.62)', backdropFilter:'blur(4px)', padding:'7px 12px',
    }}>{txt}</span>
  );
  return (
    <div ref={ref}
      onMouseDown={(e) => { setDrag(true); move(e.clientX); }}
      onTouchStart={(e) => { setDrag(true); move(e.touches[0].clientX); }}
      style={{ position:'relative', width:'100%', height, overflow:'hidden', background:'var(--ink)',
        cursor: drag ? 'ew-resize' : 'pointer', userSelect:'none', touchAction:'none' }}>
      {/* AFTER (full) */}
      <img src={after} alt={afterLabel} draggable="false" style={{ position:'absolute', inset:0, width:'100%', height:'100%', objectFit:'cover' }} />
      {tag(afterLabel, 'right')}
      {/* BEFORE (clipped to handle via clip-path so it never reflows) */}
      <div style={{ position:'absolute', inset:0, zIndex:2, clipPath:`inset(0 ${100 - pos}% 0 0)` }}>
        <img src={before} alt={beforeLabel} draggable="false" style={{ position:'absolute', inset:0, width:'100%', height:'100%', objectFit:'cover' }} />
        {tag(beforeLabel, 'left')}
      </div>
      {/* handle */}
      <div style={{ position:'absolute', top:0, bottom:0, left:`${pos}%`, transform:'translateX(-50%)', zIndex:3, display:'flex', alignItems:'center', justifyContent:'center', pointerEvents:'none' }}>
        <div style={{ position:'absolute', top:0, bottom:0, width:'2px', background:'rgba(255,255,255,0.92)' }}></div>
        <div style={{ width:'44px', height:'44px', borderRadius:'50%', background:'#fff', boxShadow:'0 2px 14px rgba(8,7,5,0.35)', display:'flex', alignItems:'center', justifyContent:'center', gap:'2px', color:'var(--ink)' }}>
          <span style={{ fontSize:'13px' }}>‹</span><span style={{ fontSize:'13px' }}>›</span>
        </div>
      </div>
    </div>
  );
}

// ── Real project records ───────────────────────────────────────
const PROJECTS = [
  // ===== PROFESSIONAL =====
  {
    slug:'residence-hall', kind:'Professional', n:'01', featured:true,
    name:'Residence Hall', subtitle:'Student Living at University of St. Thomas',
    tag:'A 210,000 SF, 480-bed residence hall anchoring the north edge of campus.',
    img:PIMG.res,
    galleryNote:'Photography of the completed hall — the campus-facing first level, lounges, study rooms and dining commons.',
    images:[
      [PIMG.stStair,'Lobby & feature stair'],
      [PIMG.stReception,'Reception & St. Thomas feature wall'],
      [PIMG.stStudy,'Daylit study & hangout lounge'],
      [PIMG.stLounge,'Resident lounge & fireplace'],
      [PIMG.stDining,'Dining commons servery'],
    ],
    details:[['Location','St. Paul, Minnesota'],['Firm','Opus Group'],['Completion Year','2020'],['Size & Scope','210,000 SF']],
    desc:[
      'Supporting a growing demand for student housing, our group was chosen to design and construct the Tommie North Residence Hall at the University of St. Thomas in St. Paul, Minnesota.',
      'The five-story, 210,000 square-foot hall sits at the north end of campus at Selby and Cleveland Avenues. It houses 480 students in roughly 255 primarily double-occupancy rooms across levels two through five — each level with study rooms, lounges and shared common spaces, and a large laundry room on the second level.',
      'The first level opens to the whole campus: a kitchen and cafeteria with cooking on display, dining for 480 that converts to flex event space, two classrooms, and a clubroom and game room. One below-grade level provides parking for 116 vehicles and racks for 173 bikes.',
      'The hall shares a below-grade wall with the Iversen Center for Faith, and a pedestrian tunnel connects it to Ireland Hall and the Chapel — strengthening the first- and second-year student community.',
    ],
    gallery:[[PIMG.stEntrance,'The Selby & Cleveland entrance'],[PIMG.stAerial,'Anchoring the north edge of campus']],
    roles:[
      {title:'Pre Design', bullets:['Conducted site feasibility studies, evaluating zoning, topography, vegetation, utilities and other constraints.','Developed programmatic requirements in collaboration with the project team to guide design objectives.','Analyzed regulatory requirements and identified necessary variances to ensure project compliance.']},
      {title:'BIM Modeling', bullets:['Created and managed 3D BIM models to support design coordination and visualization.','Integrated architectural, structural and MEP elements to identify conflicts early in the process.','Used BIM tools to produce accurate construction documents, renderings and presentations.']},
      {title:'Programming', bullets:['Analyzed interior building functions, adjacencies and operational needs.','Translated client requirements into programmatic diagrams and functional layouts.','Developed criteria to guide schematic and design decisions.']},
      {title:'Design', bullets:['Produced schematic alternatives and conceptual drawings to explore architectural solutions.','Led material selections and specification efforts to integrate aesthetics, performance and budget.','Presented visual materials to clients and stakeholders to communicate design intent.']},
    ],
    credit:'All professional photography courtesy of Opus unless otherwise noted.',
  },
  {
    slug:'life-time', kind:'Professional', n:'02', featured:true,
    name:'Life Time Athletic', subtitle:'Athletic Country Club',
    tag:'The largest Life Time in Texas — and the first to unite a club and coworking under one roof.',
    img:PIMG.life,
    gallery:[
      [PIMG.ltExtFront,'The four-story club & entry court'],
      [PIMG.ltPool,'The resort-style outdoor pool deck'],
    ],
    galleryNote:'Photography of the completed club — from the outdoor pool deck to the recovery suites, coworking and dining that make up the athletic country club.',
    images:[
      [PIMG.ltPickle,'Indoor pickleball courts'],
      [PIMG.ltRecovery,'LT Recovery & LifeClinic'],
      [PIMG.ltWork,'Life Time Work — coworking lounge'],
      [PIMG.ltReception,'Front Deck - Reception'],
      [PIMG.ltLounge,'Social booths & lounge seating'],
      [PIMG.ltCafe,'LifeCafe'],
    ],
    details:[['Location','Shenandoah, Texas'],['Firm','Life Time Architecture'],['Completion Year','2023'],['Size & Scope','170,000 SF'],['Construction Cost','$100 Million']],
    desc:[
      'Life Time Shenandoah represents the next evolution of the company\u2019s "athletic country club" model — combining fitness, wellness, lifestyle and workplace into a single integrated environment. At 170,000 square feet across four stories, it is the largest Life Time in Texas and the first in the nation to unite a full-service fitness center and coworking under one roof.',
      'The building is organized vertically. The first three floors are dedicated to wellness and recreation — expansive fitness floors, seven boutique studios, five indoor pickleball courts, hydrotherapy and recovery suites, and a full-service LifeSpa. Family amenities include indoor lap and leisure pools, Kids Academy studios and family locker rooms. A 50,000 square-foot outdoor pool deck adds lap and leisure pools, waterslides, whirlpools and a year-round bistro.',
      'The fourth floor houses a 30,000 square-foot Life Time Work — a coworking environment with private offices, conference rooms, a solarium and flexible work lounges, seamlessly integrated within the health and wellness facility.',
    ],
    roles:[
      {title:'Coordination', bullets:['Led and directed internal architectural teams and external consultants through all design and construction phases.','Facilitated communication between ownership, consultants and contractors to maintain design intent and project efficiency.','Organized project schedules and deliverables to align with client expectations and construction milestones.']},
      {title:'Standards', bullets:['Developed and maintained prototype drawing sets and detail libraries to ensure consistency across multiple projects.','Integrated industry best practices and code compliance into standard documentation.','Provided quality control reviews to align construction documents with established design and performance standards.']},
      {title:'RFI / Submittals', bullets:['Reviewed and processed project submittals, RFIs and change requests with accuracy and timely response.','Coordinated with contractors and consultants to resolve design clarifications and technical discrepancies.','Documented and tracked all communication to ensure accountability and project transparency.']},
      {title:'Construction Administration', bullets:['Oversaw field observations and punch-list reviews to ensure work adhered to design intent and specifications.','Collaborated with contractors to address on-site conditions and implement design adjustments as needed.','Supported project closeout through documentation, verification and final quality assurance reviews.']},
    ],
    credit:'All professional photography courtesy of Life Time unless otherwise noted.',
  },
  {
    slug:'our-lady-of-grace', kind:'Professional', n:'03', featured:true,
    name:'Our Lady of Grace', subtitle:'Activity Center, Gymnasium & Preschool Addition',
    tag:'A 54,744 SF design-build addition matched to a historic campus — delivered ahead of schedule.',
    img:PIMG.olg,
    gallery:[
      [PIMG.olgCastStone,'Cast stone, school medallions & cross matched to the historic campus'],
      [PIMG.olgMillwork,'Pre-function space — custom built-in millwork'],
    ],
    galleryNote:'Photography of the completed addition — the gymnasium, daylit commons and the learning spaces on display.',
    images:[
      [PIMG.olgGym,'Gymnasium with stage & convertible courts'],
      [PIMG.olgCollab,'Middle school collaboration — learning on display'],
      [PIMG.olgMaker,'Preschool & STEM / maker classroom'],
      [PIMG.olgCommons,'Daylit two-story commons & stair'],
      [PIMG.olgConcourse,'Concourse linking the gym & classroom wings'],
    ],
    details:[['Location','Edina, Minnesota'],['Firm','Opus Group'],['Completion Year','2023'],['Size & Scope','56,000 SF'],['Construction Cost','$16.8 Million']],
    desc:[
      'Established in 1949, Our Lady of Grace Catholic School is dedicated to the spiritual and academic growth of its students. When they needed an Activity Center that would also accommodate growth in the middle school and preschool, they chose our Client Direct Services to design and build an addition.',
      'The 54,744 square-foot addition is a flexible space that includes a gym with locker rooms and preschool, kindergarten and middle school classrooms. The middle school classrooms feature collaboration areas that highlight learning on display. The gym includes a stage for performances and a basketball court that converts to two smaller courts or a volleyball court.',
      'The exterior features precast walls with brick and cast stone that match the historic existing buildings on campus. School medallions and a simple cross are incorporated into the cast stone, and the pre-function space includes custom millwork and a fireplace.',
      'Using the design-build delivery method, we accelerated the construction schedule — giving Our Lady of Grace the ability to move into their new space a month before the start of the school year, ahead of schedule.',
    ],
    roles:[
      {title:'Coordination', bullets:['Directed architectural production and coordinated structural and civil consultants through the design-build process.','Ensured seamless integration between disciplines, maintaining consistency across architectural, structural and civil inputs.']},
      {title:'Pre Design', bullets:['Analyzed site constraints, zoning variances and compliance pathways.','Developed conceptual site layouts that balanced design vision, functional efficiency and regulatory constraints.']},
      {title:'Programming', bullets:['Aligned interior building functions and adjacencies with the school\u2019s operational needs and project goals.','Translated programmatic data into clear planning concepts to guide schematic design.']},
      {title:'Design', bullets:['Produced conceptual drawings and visual materials to communicate design intent and support project approvals.','Led material and finish selections balancing aesthetics, durability and cost considerations.']},
    ],
    credit:'All professional photography courtesy of Opus unless otherwise noted.',
  },

  // ===== PERSONAL =====
  {
    slug:'dorsey-lane', kind:'Personal', n:'01', featured:true,
    name:'Dorsey Lane', subtitle:'From rundown to remarkable',
    tag:'A neglected ranch taken to the studs and rebuilt by hand — designed and built with my brother.',
    img:PIMG.dorseyAfter,
    imageSlots:[['Kitchen — the reveal'],['Open living & dining'],['Primary bathroom'],['Exterior at dusk']],
    details:[['Location','Tempe, Arizona'],['Scope of Work','Full interior demo, kitchen & bath remodels, landscaping, electrical panel upgrade, break/fix'],['Role','Architect & Builder']],
    designGoals:['Exterior Refresh','Low-Maintenance Landscaping','Comfort & Affordability','Long-term Reliability','Neighborhood Value'],
    beforeAfter:{ before:PIMG.dorseyBefore, after:PIMG.dorseyAfter, beforeLabel:'Before', afterLabel:'After', caption:'Street façade — before acquisition vs. after the rebuild.' },
    vision:'Before starting construction, we measured the existing house and built a SketchUp model as a basis of design. Early renderings let us visualize the potential, explore directions, and understand how the remodel could truly transform the home — and gave us a clear framework for aligning our goals.',
    narrative:[
      'This home began as a neglected single-family structure — overbuilt on the wrong parts, underused where it mattered, and spatially disconnected from its surroundings. But beneath the clutter there was potential: strong structural bones, generous setbacks, and enough daylight to make the investment worthwhile. My brother and I acquired the property with a clear goal — design and build a home that reflects how we believe people should live.',
      'As architect and builder, we approached the project as both a creative outlet and a technical exercise. We reworked the floor plan entirely, prioritizing flow, flexibility and a stronger relationship between interior and exterior. Circulation was reorganized to eliminate dead zones, and partitions were removed to create visual connections across key living spaces.',
      'Every element was designed and built by hand. The kitchen — now the heart of the home — features a new island and generous counter space for entertaining. The bathrooms were stripped to studs and rebuilt with spatial efficiency and clean detailing. The exterior transformation included fresh paint, roof repairs and extensive landscaping. We dug every hole, poured every slab, installed every finish, and revised the schedule weekly based on real-time conditions, budget and available labor — ourselves.',
    ],
    credit:'Project by Benjamin Partridge. Renderings self-produced in SketchUp.',
  },
  {
    slug:'montreal-avenue', kind:'Personal', n:'02', featured:true,
    name:'Montreal Avenue', subtitle:'My first home — where the passion started',
    tag:'A decade-long, self-performed remodel of my first home: kitchen, baths and basement.',
    img:PIMG.montrealAfter,
    imageSlots:[['Kitchen — opened layout'],['Basement refresh'],['Remodeled bathroom'],['Living area']],
    details:[['Location','St. Paul, Minnesota'],['Scope of Work','Kitchen, bathroom & basement remodel'],['Role','Designer & Builder']],
    designGoals:['Prioritize high-impact spaces first','Improve spatial flow','Balance budget with resale value'],
    beforeAfter:[
      { before:PIMG.montrealBefore, after:PIMG.montrealAfter, beforeLabel:'Before', afterLabel:'After', caption:'Kitchen — the original galley vs. the opened, modern remodel.' },
      { before:PIMG.montrealBefore2, after:PIMG.montrealAfter2, beforeLabel:'Before', afterLabel:'After', caption:'Kitchen — dining-end view, before and after.' },
    ],
    vision:'What began as a simple effort to refresh my first home grew into a passion project for transforming spaces. This early remodel taught me the balance between creativity and practicality — how thoughtful design, even on a budget, can completely redefine how a space feels and functions.',
    narrative:[
      'This was my very first home and where my passion for remodeling truly started. Back in 2012, I had no grand plans — just a desire to make the old and tired feel a little more like home. The early projects were small: updating fixtures, replacing finishes and learning as I went. But with each improvement came more confidence and creativity.',
      'Eventually the opportunity came to open up the kitchen and living area, turning what was once compartmentalized and dark into a bright, connected space that reflected modern living. That remodel sparked something deeper — the excitement of design, construction and problem solving.',
      'Over the ten years I lived here, I completed each phase myself (with a little family help) — from the kitchen to the first-floor bath, and finally the basement refresh and new bathroom. I\u2019m proud to showcase this home as my first true project. It represents the start of the journey.',
    ],
    credit:'Project by Benjamin Partridge.',
  },
  {
    slug:'hillpark-lane', kind:'Personal', n:'03', featured:true,
    name:'Hillpark Lane', subtitle:'A client bathroom, reimagined as a primary suite',
    tag:'A client remodel: renderings, material selection and full construction — a walk-in shower as the new centerpiece.',
    img:PIMG.hillparkHero,
    beforeAfterLayout:'row',
    beforeAfter:[
      { before:PIMG.hillBefore2, after:PIMG.hillAfter2, beforeLabel:'Before', afterLabel:'After', caption:'Vanity wall — the dated double vanity and stall shower vs. the finished suite.' },
      { before:PIMG.hillBefore1, after:PIMG.hillAfter1, beforeLabel:'Before', afterLabel:'After', caption:'Tub & shower wall — from curtained tub to the glass-enclosed walk-in shower.' },
    ],
    details:[['Project','Primary bathroom remodel'],['Scope of Work','Design renderings, material selection & full construction'],['Role','Designer & Builder']],
    narrative:[
      'This project transformed an outdated, underutilized bathroom into a modern primary suite the homeowners could take pride in. My role extended beyond refreshing finishes — I worked closely with the clients through multiple design iterations, providing renderings to visualize options, guiding material selections, and ensuring every decision aligned with their vision and budget.',
      'While the general layout was kept for efficiency, the space was elevated through carefully considered updates. Outdated fixtures were replaced with clean, contemporary selections, and a walk-in shower became the new centerpiece — a feature designed to both function beautifully and evoke a sense of everyday luxury.',
    ],
    renderings:[
      [PIMG.hillparkRender2,'Design option — warm walnut vanity & brass',[[PIMG.hillR2Section,'Vanity perspective'],[PIMG.hillR2Elev,'Shower elevation']]],
      [PIMG.hillparkRender1,'Design option — grey oak vanity & matte black',[[PIMG.hillR1Section,'Vanity perspective'],[PIMG.hillR1Elev,'Shower elevation']]],
    ],
    credit:'Renderings self-produced.',
  },

  // ===== ACADEMIC =====
  {
    slug:'temporary-architecture', kind:'Academic', n:'01', featured:true, group:'Thesis',
    name:'Temporary Architecture', subtitle:'Master\u2019s Thesis',
    tag:'A high-rise massing investigation into temporary architecture and the life cycle of the tall building in the city.',
    img:PIMG.thesis,
    imageSlots:[['Thesis boards / drawings'],['Section model'],['Massing diagrams'],['Context plan']],
    details:[['Type','Master\u2019s Thesis'],['Program','M.Arch, University of Minnesota'],['Medium','Massing & context model']],
    desc:[
      'My master\u2019s thesis examines temporary architecture at the scale of the tall building — questioning how a high-rise might be conceived for adaptation and change rather than permanence, and how that logic reshapes its structure, skin and relationship to the city around it.',
      'The work was developed through massing and context models that test the tower against its urban fabric — studying how the building meets the ground, how its floors stack and shift, and how a framework of structure can host change over time.',
    ],
    credit:'Thesis work by Benjamin Partridge. (Happy to expand this writeup with your thesis abstract.)',
  },
  {
    slug:'recurve', kind:'Academic', n:'02', group:'Competition',
    name:'Recurve', subtitle:'Competition',
    tag:'A sinuous slatted pavilion turning one repeated member into an inhabitable, self-supporting public room.',
    img:PIMG.recurve,
  },
  {
    slug:'peritoneum', kind:'Academic', n:'03', group:'Competition',
    name:'Peritoneum', subtitle:'Competition',
    tag:'A stratified, membrane-like canopy of layered blue forms creating a shaded public passage.',
    img:PIMG.peritoneum,
  },
  {
    slug:'heavy-air', kind:'Academic', n:'04', group:'Studio',
    name:'Heavy Air', subtitle:'Graduate Studio',
    tag:'A massing study explored through physical model and night lighting.',
    img:PIMG.heavyAir,
  },
  {
    slug:'wetland-research', kind:'Academic', n:'05', group:'Studio',
    name:'Wetland Research', subtitle:'Graduate Studio',
    tag:'Architecture at the water\u2019s edge — model studies of light, landscape and structure.',
    img:PIMG.wetland,
  },
];

// Personal "additional projects" — shown as cards on the personal index, no detail page.
const PERSONAL_EXTRA = [
  { name:'Wood Street', img:PIMG.woodStreet, blurb:'Through collaboration with the client, I designed a multi-unit residential building for a vacant property — maximizing square footage while incorporating modern aesthetics and versatile living spaces, balancing site constraints with constructibility and market appeal.' },
  { name:'Belmont Avenue', img:PIMG.belmontAve, blurb:'A refresh of an office building\u2019s exterior to enhance curb appeal without a full remodel, as the property was intended for future sale — maximizing value while keeping costs low.' },
];

const projBySlug = (s) => PROJECTS.find((p) => p.slug === s);
const byKind = (k) => PROJECTS.filter((p) => p.kind === k);

const pmono = (extra = {}) => ({
  fontFamily:'var(--f-mono)', textTransform:'uppercase',
  letterSpacing:'0.16em', fontSize:'11px', ...extra,
});

// Striped placeholder (used where a real image isn't supplied yet).
function Placeholder({ label, kind = 'IMAGE', ratio = '4 / 3', dark }) {
  const stroke = dark ? 'rgba(243,241,236,0.16)' : 'rgba(25,22,15,0.10)';
  const bg = dark ? '#211E17' : 'var(--paper-2)';
  const fg = dark ? 'var(--on-dark-muted)' : 'var(--muted)';
  return (
    <div style={{
      aspectRatio:ratio, background:bg,
      backgroundImage:`repeating-linear-gradient(45deg, transparent, transparent 9px, ${stroke} 9px, ${stroke} 10px)`,
      border:`1px solid ${dark ? 'var(--dark-line)' : 'var(--line)'}`,
      display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', gap:'8px',
      padding:'20px', textAlign:'center',
    }}>
      <span style={pmono({ fontSize:'10px', color:'var(--accent)', letterSpacing:'0.2em' })}>{kind}</span>
      <span style={{ fontFamily:'var(--f-mono)', fontSize:'13px', color:fg, letterSpacing:'0.02em' }}>{label}</span>
    </div>
  );
}

// Top navigation.
function SiteNav({ active }) {
  const links = [['work','Work','#/work'], ['about','About','#/about'], ['resume','Resume','#/resume'], ['contact','Contact','#/contact']];
  return (
    <header style={{ position:'sticky', top:0, zIndex:50, background:'rgba(243,241,236,0.86)', backdropFilter:'blur(10px)', borderBottom:'1px solid var(--line)' }}>
      <div style={{ maxWidth:'var(--maxw)', margin:'0 auto', padding:'0 var(--gutter)', height:'64px', display:'flex', alignItems:'center', justifyContent:'space-between' }}>
        <a href="#/" style={{ textDecoration:'none', color:'var(--ink)', fontWeight:700, letterSpacing:'-0.02em', fontSize:'17px' }}>
          BENJAMIN PARTRIDGE<span style={{ color:'var(--accent)' }}>.</span>
        </a>
        <nav style={{ display:'flex', gap:'30px', alignItems:'center' }}>
          {links.map(([k, label, href]) => (
            <a key={k} href={href} style={pmono({ fontSize:'12px', textDecoration:'none', color: active === k ? 'var(--ink)' : 'var(--muted)' })}>{label}</a>
          ))}
        </nav>
      </div>
    </header>
  );
}

// Contact band — real details, repeated at the foot of most pages.
function ContactBand() {
  return (
    <section style={{ position:'relative', background:'var(--dark)', color:'var(--on-dark)', overflow:'hidden' }}>
      <TopoField tone="dark" crop="right" opacity={0.2} />
      <div style={{ position:'relative', zIndex:1, maxWidth:'var(--maxw)', margin:'0 auto', padding:'clamp(56px,8vw,104px) var(--gutter)' }}>
        <span style={pmono({ color:'var(--accent)', letterSpacing:'0.22em', display:'inline-flex', alignItems:'center', gap:'12px' })}>
          <span style={{ width:'26px', height:'1px', background:'var(--accent)' }}></span>Let&rsquo;s work together
        </span>
        <h2 style={{ fontSize:'clamp(2rem,5vw,3.6rem)', fontWeight:800, letterSpacing:'-0.04em', lineHeight:1.02, margin:'24px 0 0', maxWidth:'20ch' }}>
          Tell me what you&rsquo;re building<span style={{ color:'var(--accent)' }}>.</span>
        </h2>
        <p style={{ color:'var(--on-dark-muted)', maxWidth:'52ch', margin:'24px 0 36px', fontSize:'18px', lineHeight:1.6 }}>
          A licensed architect who designs, details and builds — across institutional projects and hands-on remodels. The fastest way to reach me is email.
        </p>
        <div style={{ display:'flex', flexWrap:'wrap', gap:'14px' }}>
          <a href={`mailto:${CONTACT.email}`} style={{ ...pmono({ fontSize:'12px' }), background:'var(--accent)', color:'#fff', padding:'16px 26px', textDecoration:'none' }}>{CONTACT.email} →</a>
          <a href="#/resume" style={{ ...pmono({ fontSize:'12px', color:'var(--on-dark)' }), border:'1px solid var(--dark-line)', padding:'16px 26px', textDecoration:'none' }}>View resume</a>
        </div>
        <div style={{ display:'flex', flexWrap:'wrap', gap:'48px', marginTop:'52px', paddingTop:'28px', borderTop:'1px solid var(--dark-line)' }}>
          {[['Email',CONTACT.email],['Phone',CONTACT.phone],['Licensed in',CONTACT.locations,'Texas license available upon request']].map(([k, v, note]) => (
            <div key={k}>
              <div style={pmono({ color:'var(--accent)', fontSize:'10px', marginBottom:'8px' })}>{k}</div>
              <div style={{ fontWeight:600, fontSize:'16px' }}>{v}</div>
              {note && <div style={pmono({ color:'var(--on-dark-muted)', fontSize:'9.5px', marginTop:'6px', letterSpacing:'0.08em' })}>{note}</div>}
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

function SiteFooter() {
  return (
    <footer style={{ background:'var(--dark-2)', color:'var(--on-dark-muted)', borderTop:'1px solid var(--dark-line)' }}>
      <div style={{ maxWidth:'var(--maxw)', margin:'0 auto', padding:'28px var(--gutter)', display:'flex', flexWrap:'wrap', gap:'18px', justifyContent:'space-between', alignItems:'center' }}>
        <span style={{ fontWeight:700, color:'var(--on-dark)', letterSpacing:'-0.02em' }}>BENJAMIN PARTRIDGE<span style={{ color:'var(--accent)' }}>.</span></span>
        <span style={pmono({ fontSize:'10px' })}>Registered Architect · Licensed in MN &amp; AZ · ©2026</span>
      </div>
    </footer>
  );
}

Object.assign(window, { PROJECTS, PERSONAL_EXTRA, CONTACT, projBySlug, byKind, PIMG, pmono, Placeholder, TopoField, BeforeAfter, SiteNav, ContactBand, SiteFooter });
