# Vibn — Marketing Site

Production Vite + React 18 + Tailwind CSS implementation of the Vibn marketing
homepage and beta-invite signup page.

## Stack

- **Vite 6** — dev server, HMR, multi-page build
- **React 18** — function components + hooks, no router (two static entries)
- **Tailwind CSS 3** — utility classes + a small `@layer components` block for
  things that don't compress cleanly to utilities (gradients, pseudo-elements,
  custom shadows)
- **No CSS-in-JS, no UI library** — design tokens live as CSS custom properties
  so a tweak panel or theme switcher can runtime-swap the accent palette
- **Geist + Geist Mono** — loaded from Google Fonts in each entry HTML

## Getting started

```bash
npm install
npm run dev          # http://localhost:5173 (homepage)
                     # http://localhost:5173/beta.html (signup)
npm run build        # production build → dist/
npm run preview      # serve the built bundle
```

## Project layout

```
vibn-app/
├── index.html              ← homepage entry
├── beta.html               ← beta-signup entry
├── vite.config.js          ← multi-page input config
├── tailwind.config.js      ← design tokens + named animations
├── postcss.config.js
├── public/
│   └── logo-black.png      ← favicon (V_ mark on black)
└── src/
    ├── main.jsx            ← mounts <App />
    ├── beta-main.jsx       ← mounts <BetaApp />
    ├── styles.css          ← Tailwind + tokens + keyframes + .btn / .card / .logo-mark
    ├── App.jsx             ← homepage composition
    ├── BetaApp.jsx         ← signup-page composition
    ├── lib/
    │   └── primitives.jsx  ← Logo, LogoMark, Arrow, Eyebrow, Glow, TrustStrip
    └── components/
        ├── Nav.jsx
        ├── Hero.jsx                ← Reddit-quote / promise variants + prompt input
        ├── Wall.jsx                ← faux chat-window "homework wall" scene
        ├── CrossedOut.jsx          ← animated strike-through term wall
        ├── Journey.jsx             ← 4-step path + "where others stop" marker
        ├── Audience.jsx            ← 3 audience cards w/ Reddit-style quotes
        ├── Closing.jsx
        ├── Footer.jsx
        ├── LaunchModal.jsx         ← hero prompt-submit modal
        └── beta/
            ├── BetaForm.jsx        ← 5-step form
            ├── Confirmed.jsx       ← submitted state w/ queue card + referral
            └── Benefits.jsx        ← "what you get inside" trio
```

## Design tokens

All colors are exposed as CSS custom properties (see `src/styles.css`,
`:root` block) and aliased in `tailwind.config.js → theme.extend.colors`:

| Tailwind class | CSS var          | Default                          |
|---|---|---|
| `bg-bg`         | `--c-bg`         | `oklch(0.155 0.008 60)`          |
| `bg-bg-1`       | `--c-bg-1`       | `oklch(0.185 0.009 60)`          |
| `text-fg`       | `--c-fg`         | `oklch(0.97 0.005 80)`           |
| `text-fg-dim`   | `--c-fg-dim`     | `oklch(0.78 0.006 80)`           |
| `text-fg-mute`  | `--c-fg-mute`    | `oklch(0.58 0.006 80)`           |
| `text-fg-faint` | `--c-fg-faint`   | `oklch(0.42 0.006 80)`           |
| `text-accent`   | `--c-accent`     | `oklch(0.74 0.175 35)` (coral)   |
| `bg-accent`     | `--c-accent`     | (same)                           |
| `border-hairline` | `--c-hairline` | `oklch(0.32 0.010 60 / 0.55)`    |
| `text-ok`       | `--c-ok`         | `oklch(0.78 0.16 155)`           |

To re-theme at runtime, set the variables on `:root` from JS:

```js
document.documentElement.style.setProperty("--c-accent", "#9ee649");
document.documentElement.style.setProperty("--c-accent-glow", "#9ee64959");
document.documentElement.style.setProperty("--c-accent-fg",  "#0f1408");
```

## Type scale

Fluid `clamp()` sizes are used inline:

- Hero headline: `clamp(44px, 7.4vw, 104px)`
- Section H2: `clamp(36px, 4.8vw, 64px)`
- Body / sub: `clamp(16–17px, 1.6–2.2vw, 19–28px)`

Geist is the body face; Geist Mono is reserved for tags, eyebrows, code, and
"trust strip" details.

## Animations

Keyframes are defined once in `styles.css`; some are also registered as named
Tailwind utilities (`animate-caret-blink`, `animate-pulse-ok`, etc.) for
ergonomics. Anything not in the config uses an inline `style={{ animation: ... }}`
with the keyframe name.

## Notes & next steps

- **No router.** Homepage links to `/beta.html` directly. Drop in
  `react-router-dom` if you need client-side navigation between more pages.
- **Form submission is a stub.** `BetaForm` just waits 700 ms and toggles to
  the confirmed state. Wire to your real endpoint (e.g. `fetch("/api/invite", …)`).
- **Queue position is deterministic on email.** Replace with the real one from
  your backend.
- **The hero "Live from minute one" pill is currently not rendered** in this
  port (the marketing prototype defaults it off). Re-add by uncommenting the
  pill in `Hero.jsx` if you want it back.
- **The Tweaks panel from the prototype is not ported.** Tweaks are a design-time
  tool; runtime theming is enough via the CSS-var token system.

## Browser support

Uses `oklch()`, `text-wrap: balance`, and `backdrop-filter`. Safari 15.4+,
Chrome 111+, Firefox 113+.
