Files
agency/LLM.md
zeekay 2dda1f4299 Load GA4 after the page, from hanzo.yml's ID
The head configured GA4 behind a gtag.js script tag, so a network that
dropped Google's host held the load event hostage.  Configure it inline
and fetch gtag.js once the page has loaded, with a linker across the
Hanzo domains a visit crosses, and the same GPC/DNT opt-out the site's
own telemetry honors.

The ID lived in both hanzo.yml and index.html.  vite.config.ts now reads
it from hanzo.yml into index.html.

ScrollToTop sent its own page_view on every route change, on top of
GA4's history listener, so each page counted twice.  Drop it.
2026-09-28 10:38:15 -07:00

7.3 KiB

Hanzo Agency Website Project

Project Overview

This project is a React application built with TypeScript, Vite, shadcn-ui, and Tailwind CSS. It's a portfolio website for Hanzo AI agency showcasing their case studies and services.

How it ships

.github/workflows/deploy.yml on GitHub (linux-amd64, the platform's runners; the forge copy is a pull mirror with Actions off, so .hanzo/workflows runs nowhere): build dist -> POST /v1/projects/agency/deploy (202, carrying a presigned upload grant) -> POST each file under that grant -> POST .../complete with the file manifest as keys. The bytes never pass through the API; BodyLimit is 16 MiB. No GitHub Pages, no Cloudflare Pages, and no image -- a static export has no compute to run.

This repo holds NO S3 credential. The grant is confined to this site's prefix and expires in 30 minutes, so a leak here cannot reach another org's site the way the old shared bucket keys could. Deletion rides the manifest: the grant permits writes only, so cloud prunes the prefix against keys. The deploy key is in KMS; the workflow holds only the hanzoai org's KMS_CLIENT_ID/KMS_CLIENT_SECRET.

Telemetry is @hanzo/event (src/analytics.tsx, mounted inside the router in src/App.tsx) posting to api.hanzo.ai/v1/event. One client for pageviews, events and errors: no Meta Pixel, no Plausible, no separate error SDK.

GA4 is hanzo.agency's own stream, stated once in hanzo.yml (analytics.googleAnalyticsId); vite.config.ts writes it into index.html's %GA%. The head configures it inline and fetches gtag.js after load. GA4's history listener counts route changes, so no component sends page_view.

Project Structure

  • /src: Main source code
    • /components: Reusable UI components
    • /pages: Top-level page components
    • /data: Static data files
      • /data/case-studies: Individual case study data files
      • /data/caseStudies.ts: Main file that imports and exports all case studies
    • /hooks: Custom React hooks
    • /utils: Utility functions
    • /types: TypeScript type definitions
  • /components: Top-level components (Header, Navigation)
  • /styles: CSS files
    • /styles/navigation.css: Navigation menu styling
    • /styles/variables.css: CSS variables

Routing System

The application uses react-router-dom for routing. The main routes are defined in App.tsx:

  • /: Homepage
  • /case-studies: All case studies overview
  • /case-studies-alt: Alternative case studies view
  • /case-study/:id: Individual case study pages (using the CaseStudy component)
  • /contact: Contact page
  • /subscribe: Newsletter subscription
  • /onboarding: Client onboarding form

Case Studies System

Case studies are a key part of the website and follow a consistent pattern:

  1. Data Organization:

    • Each case study has its own file in src/data/case-studies/ directory:
      • damon.ts
      • bellabeat.ts
      • triller.ts
      • unikrn.ts
      • cover.ts
      • casper.ts
      • myle.ts
    • All case studies are imported and exported together in src/data/caseStudies.ts
  2. Rendering Components:

    • src/pages/CaseStudy.tsx: Dynamic page component that renders any case study based on the URL parameter
    • src/components/CaseStudyPage.tsx: Component for rendering a case study's full content
    • src/components/CaseStudy.tsx: Component used on the homepage to showcase featured case studies
  3. Case Study IDs:

    • Each case study file exports an object with an id property matching its filename
    • IDs are used consistently throughout the application
    • The ID damon-motorcycles is used in data but sometimes referred to as damon in UI components
  4. Routing Structure:

    • All case studies are accessed via the route pattern: /case-study/:id
    • The ID in the route corresponds to the ID in the case study file

UI Components and Navigation

  1. Header and Navigation:

    • Header component (/components/Header.tsx) contains the main navigation
    • Navigation component (/components/Navigation.tsx) implements the main menu
    • Desktop navigation includes dropdown menus for Services and Solutions sections
    • Mobile navigation collapses into a hamburger menu at screen widths below 768px
  2. Navigation Structure:

    • The navigation uses a combination of CSS classes for styling
    • Dropdown menus are positioned absolutely in relation to the header
    • CSS variables in variables.css define key measurements, including header height
    • Navigation is responsive with different behaviors at desktop and mobile breakpoints
  3. UI Design Pattern:

    • Navigation now uses shadcn's NavigationMenu component for dropdowns
    • Navigation content is stored in a separate data file (src/data/navigationItems.ts)
    • Hero headline updated to "AI is our super power"
    • Fixed header with proper dropdown positioning
    • Navigation dropdown fixes applied via CSS overrides to ensure visibility on hover
    • Multiple CSS files used to fix dropdown issues:
      • NavigationDropdownFix.css - Basic hover and pointer-events fixes
      • navbar-dropdown-final-fix.css - Comprehensive overflow and z-index fixes
      • navigation-dropdown-override.css - High-specificity overrides

Services Section Redesign

  • Updated Services component to match two-card pricing layout from design mockup
  • Left card: Dark green (#3a4a42) with flexible pricing information
  • Right card: Beige (#f5f3e9) with included features checklist
  • Responsive design: Cards stack vertically on mobile/tablet, side-by-side on desktop
  • Service categories displayed in responsive grid below the main cards
  • Custom CSS file (Services.css) for precise styling and animations
  • Added CreativePortfolioCarousel component (src/components/CreativePortfolioCarousel.tsx)
  • Uses shadcn/ui carousel component with Embla Carousel for smooth scrolling
  • Features 13 creative works spanning design and AI capabilities:
    • Design Work: Presentation Design, Branding Services, Illustration, Ad Creative, Social Media, Video Production, Web Design, Motion Design, 3D/AR Design
    • AI Solutions: AI Enhanced Creative, AI Pipeline Development, LLM Fine Tuning, RAG System Implementation
  • Images stored in public/images/carousel/ with exact filenames from desktop
  • Responsive layout: 3 items on desktop, 2 on tablet, 1 on mobile
  • Category badges overlay on images for easy identification
  • Hover effects with scale transformation and shadow enhancement
  • Mobile-friendly with swipe gesture support and scroll indicator
  • Added to homepage between Services and AICapabilities sections

AI Capabilities Section

  • Enhanced AICapabilities component (src/components/AICapabilities.tsx) with improved image handling
  • Features three main capabilities cards with SVG graphics:
    • Intelligent Systems (ai-nodes.svg)
    • Predictive Analytics (data-wave.svg)
    • Custom Development (code-blocks.svg)
  • Displays the frontier models and platforms we build on (a tools-we-use strip, not a partner claim)
  • The three feature-card graphics render as plain <img>. They deliberately carry no loading="lazy" and no skeleton: a lazy image that starts life display:none never fires onLoad, so the skeleton used to latch on forever and the cards showed empty. These SVGs are bundled — there is nothing to wait for and nothing to fall back to.
  • All images stored in public/images/logo/ and public/images/graphics/