# Portfolio (design showcase)

A modern designer/agency portfolio. Project cards with hover effects,
smooth scroll, sticky nav, dark hero with bright accent. This is the
example to reach for when the user wants something visually impressive
— a freelancer site, a small studio, a personal brand page.

The pattern: each project is a sub-page with metadata (title, tagline,
year, tags, hero color). The main page reads `cross_page_metadata.json` and renders
project cards. Click a card → drill into a case study page.

## The script

```python
import requests
import time

API = "https://api.uat-beam.page"
TOKEN = "<your-token>"
H = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}

slug = "studio-nova"

# 1. Create the project
requests.post(f"{API}/projects", headers=H, json={
    "slug": slug,
    "context": "Studio Nova — independent design studio in London. Portfolio site with project case studies.",
})
time.sleep(2)

# 2. Brand it — black, white, electric accent
requests.put(f"{API}/projects/{slug}/assets/tailwind-config.js", headers=H, json={
    "content": (
        'tailwind.config = { theme: { extend: { '
        'colors: { '
        'ink: "#0a0a0a", '
        'paper: "#fafaf9", '
        'accent: "#ff5436" '
        '}, '
        'fontFamily: { '
        'display: ["Space Grotesk", "sans-serif"], '
        'mono: ["JetBrains Mono", "monospace"] '
        '} '
        '} } }'
    ),
})

requests.put(f"{API}/projects/{slug}/assets/styles.css", headers=H, json={
    "content": (
        "@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap');\\n"
        "@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&display=swap');"
    ),
})

# 3. Create the project case study pages
projects = [
    {
        "slug": "northwind",
        "title": "Northwind",
        "tagline": "Brand identity for a sustainable airline",
        "year": "2025",
        "tags": ["Brand", "Identity", "Print"],
        "color": "#2d4a3e",
    },
    {
        "slug": "loop-magazine",
        "title": "Loop Magazine",
        "tagline": "Editorial design for a quarterly culture journal",
        "year": "2024",
        "tags": ["Editorial", "Print", "Typography"],
        "color": "#d4af37",
    },
    {
        "slug": "midnight-radio",
        "title": "Midnight Radio",
        "tagline": "Album artwork and merchandise for an indie label",
        "year": "2024",
        "tags": ["Music", "Illustration", "Merchandise"],
        "color": "#8b1538",
    },
]

for p in projects:
    requests.post(f"{API}/projects/{slug}/pages", headers=H, json={
        "slug": p["slug"],
        "notes": p["title"],
    })
    time.sleep(0.3)

    requests.put(
        f"{API}/projects/{slug}/pages/{p['slug']}/metadata",
        headers=H,
        json={
            "title": p["title"],
            "tagline": p["tagline"],
            "year": p["year"],
            "tags": p["tags"],
            "color": p["color"],
        },
    )
    time.sleep(0.3)

# 4. The main page — sticky nav, big hero, project grid with hover
main_html = """<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Studio Nova — independent design studio</title>
  <script src="https://cdn.tailwindcss.com"></script>
  <script src="/assets/tailwind-config.js"></script>
  <link rel="stylesheet" href="/assets/styles.css">
  <style>
    html { scroll-behavior: smooth; }
    body { font-family: 'Space Grotesk', sans-serif; }
    .marquee { animation: scroll 30s linear infinite; }
    @keyframes scroll {
      0% { transform: translateX(0); }
      100% { transform: translateX(-50%); }
    }
    .project-card { transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1); }
    .project-card:hover { transform: translateY(-8px); }
    .project-card:hover .project-color { width: 100%; }
    .project-color { transition: width 0.5s cubic-bezier(0.16, 1, 0.3, 1); }
  </style>
</head>
<body class="bg-paper text-ink"
      x-data="{ projects: [] }"
      x-init="
        fetch('/cross_page_metadata.json').then(r => r.json())
          .then(d => projects = d.pages.filter(p => p.slug !== '/'))
      ">

  <!-- Sticky nav -->
  <nav class="sticky top-0 z-50 bg-paper bg-opacity-90 backdrop-blur-md border-b border-stone-200">
    <div class="max-w-6xl mx-auto px-6 py-4 flex justify-between items-center">
      <a href="/" class="font-display font-bold text-xl tracking-tight">Studio Nova<span class="text-accent">.</span></a>
      <div class="hidden md:flex gap-8 font-mono text-sm">
        <a href="#work" class="hover:text-accent transition">Work</a>
        <a href="#about" class="hover:text-accent transition">About</a>
        <a href="#contact" class="hover:text-accent transition">Contact</a>
      </div>
    </div>
  </nav>

  <!-- Hero -->
  <section class="min-h-[80vh] flex flex-col justify-center px-6 max-w-6xl mx-auto">
    <p class="font-mono text-xs uppercase tracking-widest text-stone-500 mb-6">
      [ Independent design studio · est. 2019 · London ]
    </p>
    <h1 class="font-display font-bold text-6xl md:text-8xl leading-[0.95] tracking-tight mb-8">
      We make brands<br>
      <span class="text-accent">people remember</span>.
    </h1>
    <p class="font-display text-xl text-stone-600 max-w-xl">
      Brand identity, editorial, and packaging for clients who care about the details.
    </p>
  </section>

  <!-- Marquee -->
  <div class="overflow-hidden py-6 border-y border-stone-200 bg-ink text-paper">
    <div class="marquee whitespace-nowrap font-display text-2xl">
      <span class="mx-8">Brand Identity</span><span class="text-accent">●</span>
      <span class="mx-8">Editorial</span><span class="text-accent">●</span>
      <span class="mx-8">Packaging</span><span class="text-accent">●</span>
      <span class="mx-8">Typography</span><span class="text-accent">●</span>
      <span class="mx-8">Art Direction</span><span class="text-accent">●</span>
      <span class="mx-8">Brand Identity</span><span class="text-accent">●</span>
      <span class="mx-8">Editorial</span><span class="text-accent">●</span>
      <span class="mx-8">Packaging</span><span class="text-accent">●</span>
      <span class="mx-8">Typography</span><span class="text-accent">●</span>
      <span class="mx-8">Art Direction</span><span class="text-accent">●</span>
    </div>
  </div>

  <!-- Selected work -->
  <section id="work" class="py-24 px-6 max-w-6xl mx-auto">
    <div class="flex justify-between items-end mb-12">
      <div>
        <p class="font-mono text-xs uppercase tracking-widest text-stone-500 mb-2">[ 01 ]</p>
        <h2 class="font-display font-bold text-4xl">Selected work</h2>
      </div>
      <p class="font-mono text-sm text-stone-500" x-text="'(' + projects.length + ')'"></p>
    </div>

    <div class="space-y-4">
      <template x-for="(p, i) in projects" :key="p.slug">
        <a :href="p.url"
           class="project-card block bg-white rounded-lg overflow-hidden border border-stone-200 group">
          <div class="grid grid-cols-12 gap-6 p-8 items-center">
            <div class="col-span-1 font-mono text-sm text-stone-400" x-text="(i + 1).toString().padStart(2, '0')"></div>
            <div class="col-span-7">
              <h3 class="font-display font-bold text-3xl mb-1" x-text="p.metadata.title"></h3>
              <p class="text-stone-500" x-text="p.metadata.tagline"></p>
            </div>
            <div class="col-span-2 flex flex-wrap gap-1">
              <template x-for="tag in p.metadata.tags">
                <span class="font-mono text-xs uppercase tracking-wider text-stone-500" x-text="tag"></span>
              </template>
            </div>
            <div class="col-span-1 font-mono text-sm text-stone-400 text-right" x-text="p.metadata.year"></div>
            <div class="col-span-1 text-right">
              <span class="inline-block w-8 h-8 rounded-full group-hover:scale-110 transition-transform"
                    :style="'background:' + p.metadata.color"></span>
            </div>
          </div>
          <div class="project-color h-1 w-12" :style="'background:' + p.metadata.color"></div>
        </a>
      </template>
    </div>
  </section>

  <!-- About -->
  <section id="about" class="py-24 px-6 bg-ink text-paper">
    <div class="max-w-4xl mx-auto">
      <p class="font-mono text-xs uppercase tracking-widest text-stone-500 mb-2">[ 02 ]</p>
      <h2 class="font-display font-bold text-4xl mb-12">About</h2>
      <p class="font-display text-2xl md:text-3xl leading-tight mb-8 text-stone-200">
        We're a small studio that does big work for clients who want
        their brand to feel as good as it looks. No process decks, no
        mood-board theatre — just considered design that earns its keep.
      </p>
      <div class="grid md:grid-cols-3 gap-8 mt-16 font-mono text-sm">
        <div>
          <p class="text-stone-500 mb-2">FOUNDED</p>
          <p class="text-paper">2019, London</p>
        </div>
        <div>
          <p class="text-stone-500 mb-2">TEAM</p>
          <p class="text-paper">Three designers, one strategist</p>
        </div>
        <div>
          <p class="text-stone-500 mb-2">CLIENTS</p>
          <p class="text-paper">Independent brands worldwide</p>
        </div>
      </div>
    </div>
  </section>

  <!-- Contact -->
  <section id="contact" class="py-24 px-6 max-w-4xl mx-auto text-center">
    <p class="font-mono text-xs uppercase tracking-widest text-stone-500 mb-2">[ 03 ]</p>
    <h2 class="font-display font-bold text-5xl md:text-7xl mb-8 leading-none">
      Let's make<br>something <span class="text-accent">good</span>.
    </h2>
    <a href="mailto:hello@studionova.example"
       class="inline-block font-display text-2xl border-b-2 border-accent pb-1 hover:text-accent transition">
      hello@studionova.example
    </a>
  </section>

  <footer class="py-12 px-6 border-t border-stone-200">
    <div class="max-w-6xl mx-auto flex justify-between items-center font-mono text-xs text-stone-500">
      <p>&copy; 2026 Studio Nova</p>
      <p>London &middot; UK</p>
    </div>
  </footer>

  <script src="https://cdn.jsdelivr.net/npm/alpinejs@3/dist/cdn.min.js" defer></script>
</body>
</html>
"""

requests.put(
    f"{API}/projects/{slug}/pages/_root/files/index.html",
    headers=H,
    json={"content": main_html},
)

print(f"Done. Live at https://{slug}.uat-beam.page")
```

## What makes it look like this

- **Sticky translucent nav** with backdrop blur. One line of CSS,
  feels expensive.
- **Massive headline** (8xl) with the accent colour highlighting the
  key phrase. Eye candy.
- **Mono accents** for tags, numbers, labels. Pairs perfectly with a
  big display sans like Space Grotesk.
- **Numbered sections** (`[ 01 ]`, `[ 02 ]`) — editorial trick that
  makes a portfolio feel like a magazine.
- **Marquee bar** between hero and work — animated, draws the eye.
- **Project rows, not cards.** Each project is a wide horizontal row
  with title, tags, year, and a coloured dot. Hover lifts the row and
  expands the colour bar at the bottom. Subtle but reads as polished.
- **Inverse-coloured About section.** Black background, white text.
  Breaks the rhythm of the page and makes the studio's voice feel
  louder.
- **Big email CTA at the bottom** instead of a contact form. For a
  studio site, an email link is more in keeping with the vibe.

## Adapt it

This same shell works for:

- A photographer's portfolio (swap project rows for image grids)
- A consultant's site (swap project rows for case study summaries)
- A musician's site (rows become tracks or releases)
- A writer's site (rows become essays)

The metadata pattern is the same — each item is a sub-page with
title, tagline, year, tags. The main page just renders them in
whatever shape the design wants.
