AkurAI Build
Menu

Public repository / c2f43d4988686a5b5142bb6dd4255f9b473792dc

AkurAI-Notes / .plan/epics/E02-landing-page.md

Latest change f1d9b8cc6ff48113d891a2d95fd79b2c49f1f4ff - Initial plan and .plan directory by Claude Code

# E02 — Landing page + GPT-5.5 icons

## Goal

A public landing page at `/` that matches the visual language of other AkurAI
apps, plus a generated Notes-app icon set. The landing page is the only public
page; everything beyond `/auth/login` requires a session.

## Reference visuals

Other AkurAI landing pages to mirror:
- `https://akurai-framework.olibuijr.com/` — the Framework site itself (hero,
  features grid, install widget, GitHub CTA)
- `https://auth.olibuijr.com/` — IDP landing (subtle hero + features)
- `https://akurai-platform.olibuijr.com/` — Platform landing (the suite
  directory)

The shared palette (AkurAI dark-first, from `~/Projects/AkurAI-Framework/site/frontend/styles.css`):
- `--bg #060912`
- `--panel #0e162b`
- `--fg #eef3ff`
- `--accent #5b8cff` → `--accent-2 #7af0d0` (gradient)

## Steps

### 1. Read the canonical landing template + styles

**Read:**
- `~/Projects/AkurAI-Framework/site/frontend/index.html` (landing page —
  hero + features grid + install widget + status pill)
- `~/Projects/AkurAI-Framework/site/frontend/header.html` and `footer.html`
- `~/Projects/AkurAI-Framework/site/frontend/styles.css` (extract custom
  properties into Notes' own `site/frontend/styles.css`)
- `~/Projects/AkurAI-Framework/site/frontend/theme.js`, `themes.css`,
  `themes.json` (the named multi-theme switcher — copy verbatim; it's free)
- `~/Projects/AkurAI-Framework/site/frontend/app.js` (the per-page glue +
  readyState guard pattern)
- `~/Projects/akurai-crm/site/frontend/index.html` (a sister app's landing —
  for the slightly different product-hero layout; CRM uses more product-UI
  chrome than Framework's marketing-page hero)

Then write Notes' `site/frontend/index.html` (the public landing). Use the
`{% include "header" %}` / `{% include "footer" %}` template composition.

The content blocks to mirror from the Framework's index (matched 1:1 to
Notes' value props):

| Framework hero block | Notes equivalent |
|----------------------|------------------|
| `eyebrow` — "vX.X.X · The pure-Rust web runtime" | "v0.0.1 · Private notes, on-device search" |
| `headline` — "Build apps in pure Rust" | "Your notes. **Yours.**" |
| `lede` — framework's value prop | "Private notes with semantic search — running on your own server, never scanned by a third party. Like a paper notebook that searches itself." |
| `cta_primary` — "Get started" | "Sign in with AkurAI" (href `/auth/login`) |
| `cta_secondary` — "GitHub" | "View source" → GitHub repo once published |
| `install-widget` (the curl pipe) | Drop this — Notes has no installer (it's a hosted app, not a CLI) |
| `#health` status pill | Keep — points at Notes' own `/api/health` |

Features grid (3-4 cards, same shape as Framework's `.features`):

1. **On-device semantic search** — "Type 'meeting follow-up' and find every
   note you wrote about it — including that one titled 'recap with Anna'. The
   embedding model runs on the same machine as your notes." icon: a small lightbulb or
   magnifier.
2. **Single-binary, self-hosted** — "One static binary, your SQLite store, your
   TLS-certificate-carrying nginx. No SaaS, no 'going down for maintenance at
   2am'." icon: server/rack.
3. **Open AI protocol** — "The notes API speaks MCP over HTTPS — ask Claude
   Desktop or any other MCP client to search your notes for you." icon: plug
   puzzle.
4. **Native-ESM, no build step** — "Built on AkurAI-Framework. The editor is
   hand-written ES modules; the binary serves them directly. No node_modules,
   no transpiler, no second process." icon: the binary/`0101` mark.

### 2. Compose the page with server-rendered template syntax

Notes' `site/frontend/index.html`:

```html
{% include "header" %}
<main>
  <section class="hero">
    <div class="container hero-inner">
      <span class="eyebrow">{{ version }} · {{ t "hero.eyebrow_suffix" }}</span>
      <h1>{{ t "hero.headline_pre" }} <span class="grad">{{ t "hero.headline_highlight" }}</span></h1>
      <p class="lede">{{ t "hero.lede" }}</p>
      <div class="btn-row">
        <a class="btn btn-primary" href="/auth/login">{{ t "hero.cta" }}</a>
        <a class="btn btn-secondary" href="https://github.com/olibuijr/AkurAI-Notes">{{ t "hero.source" }}</a>
      </div>
      <div id="health" class="status" aria-live="polite">{{ t "hero.status_checking" }}</div>
    </div>
  </section>

  <section class="container features">
    <article class="feature">
      <span class="feature-icon" style="--icon: url('/img/icon-search.svg')" aria-hidden="true"></span>
      <h3>{{ t "feature.search.title" }}</h3>
      <p>{{ t "feature.search.body" }}</p>
    </article>
    …three more…
  </section>
</main>
{% include "footer" %}
```

The translation strings come from `site/backend/locales/en.toml` (the CRM has
this file; copy the shape). `{{ version }}` is populated by the SSR context
(see `crates/cli/src/routes.rs` — the CLI's SSR context assembly).

### 3. Generate the app icons via `codex exec` (GPT-5.5)

Generate two icon assets:
- `site/frontend/icon-notes.png` — 512×512 PNG, brand-yellow mint accent over
  navy background, a stylized "stack of cards with a magnifying-glass-over-them"
  mark at ~70% scale, padded to 25% margin on all sides. Pixel-perfect, crisp.
- `site/frontend/favicon.svg` — simplified single-colour version of the icon
  using only `--fg` and `--accent` paths.
- `site/frontend/apple-touch-icon.png` (180×180) — same icon square-solid.

The canonical AkurAI-icon prompt pattern (copy from what other AkurAI apps do;
the CRM has `icon-notes.png` is missing for now — that's literally why we're
generating one — the pattern is the same as how the AkurAI-Framework icon at
`https://akurai-framework.olibuijr.com/img/icon-binary.svg` was generated):

```bash
codex exec --model gpt-5.5 \
"Design a 512x512 px app icon for AkurAI-Notes, an upcoming privacy-focused
note-taking web app. Constraints:
- Background: solid #060912 (AkurAI deep navy).
- Foreground: rounded 'stack of three cards' silhouette, each card a thin
  rounded rectangle (0e162b fill with a 1.5px stroke in #5b8cff).
- Overlay: a magnifying-glass silhouette intersecting the stack, stroke
  #7af0d0, 2.5px, lens about 60% of the card stack width.
- Style: flat, 2-px-radius corner radii, no gradients, no shadows, no text.
- Output PNG file at /home/olafurbui/Projects/AkurAI-Notes/site/frontend/icon-notes.png.
- Generate a matching favicon.svg (single colour #7af0d0, single path,
  simplified silhouette of the same mark, 24x24 viewBox) at
  /home/olafurbui/Projects/AkurAI-Notes/site/frontend/favicon.svg.
- Generate the 180x180 apple-touch-icon.png at
  /home/olafurbui/Projects/AkurAI-Notes/site/frontend/apple-touch-icon.png
  (no rounded card background; the mark only, on transparent."
```

The codex CLI runs GPT-5.5 in the foreground; it writes the files directly. If
it errors, retry once with the same prompt. If still failing, fall back to
generating the SVG only and `rsvg-convert` to PNG (the VectorArt skill at
`~/.claude/skills/VectorArt/` documents this fallback + the bare-bones SVG
prompt shape; ping it as a Skill only if codex fails twice).

### 4. Verify visually (Interceptor is down on this host; use Camoufox)

Per AGENTS.md, web verification on this box uses Interceptor or Camoufox. The
Interceptor skill notes the binary lives at `~/Projects/interceptor`, and
launches `DISPLAY=:1 chromium --load-extension=...`. Since Notes isn't live
yet, the verification is local:

```bash
# Start Notes locally for the screenshot (requires E01 done so the binary compiles)
akurai-notes serve --frontend site/frontend --port 9811 --db /tmp/notes-test.db --embed-url '' &
NOTES_PID=$!
sleep 1

# Open the page in Camoufox and screenshot it (or agent-browser this is fine
# — Notes is a local server, not a bot-blocked destination):
npx -y agent-browser screenshot http://127.0.0.1:9811 --dest /tmp/notes-landing.png
xdg-open /tmp/notes-landing.png   # only if X is available

kill $NOTES_PID
```

Note: this verification depends on E01 leaving a working CLI skeleton. **If
the CLI doesn't `serve` yet (cmd_serve.rs is a stub), the screenshot step is
deferred to E08 verify.** In that case do the visual check by opening
`site/frontend/index.html` directly in a browser; render will be missing
`{{ version }}` etc tokens but layout + icon position should be visually
correct.

### 5. Update `page.json` header version badge

`site/backend/page.json` was filled in by `akurai new` with default content.
We need Notes-shaped metadata. Replace the whole file with the minimal shape
from `CODE-PATTERNS.md §10` and set `version` at `v0.0.1`.

### 6. Update `routes.json`

`akurai new` may have left a default `routes.json`; replace with the Notes
shape. Minimal version (more pages added in E04):

```json
{
  "routes": [
    { "path": "/",        "template": "index.html", "layout": "none" },
    { "path": "/auth/login",    "handler": "auth_login",    "method": "GET" },
    { "path": "/auth/callback", "handler": "auth_callback", "method": "GET" },
    { "path": "/auth/logout",   "handler": "auth_logout",   "method": "GET" },
    { "path": "/app",           "template": "app.html",      "layout": "default" },
    { "path": "/api/health",    "handler": "health",         "method": "GET" },
    { "path": "/api/_meta",     "handler": "meta",          "method": "GET" }
  ]
}
```

## Files touched

```
site/frontend/index.html         ← rewritten as Notes landing
site/frontend/styles.css        ← brand tokens copied from Framework verbatim
site/frontend/header.html       ← copied from Framework, brand swapped
site/frontend/footer.html       ← copied from Framework, brand swapped
site/frontend/app.js            ← copied from Framework (the readyState-guard glue)
site/frontend/theme.js, themes.css, themes.json   ← copied verbatim
site/frontend/signals.js, islands.js, spa.js      ← copied verbatim from Framework
site/frontend/img/                                             ← new dir
site/frontend/img/icon-search.svg     ← fine SVG for feature icon #1
site/frontend/img/icon-server.svg
site/frontend/img/icon-plug.svg
site/frontend/img/icon-binary.svg
site/frontend/icon-notes.png           ← 512×512 GPT-5.5 output
site/frontend/apple-touch-icon.png     ← 180×180 GPT-5.5 output
site/frontend/favicon.svg             ← 24×24 GPT-5.5 output
site/backend/page.json          ← Notes metadata
site/backend/routes.json        ← Notes route table (updated)
site/backend/locales/en.toml    ← Notes translation strings
```

## Acceptance

```bash
# Files exist:
[ -f ~/Projects/AkurAI-Notes/site/frontend/index.html ] && echo "PASS: landing exists"
[ -f ~/Projects/AkurAI-Notes/site/frontend/icon-notes.png ] && echo "PASS: icon png exists"
[ "$(file -b --mime-type ~/Projects/AkurAI-Notes/site/frontend/icon-notes.png)" = "image/png" ] \
  && echo "PASS: icon is PNG"
[ -f ~/Projects/AkurAI-Notes/site/frontend/favicon.svg ] && echo "PASS: favicon exists"

# Page.json is the Notes shape:
jq -r '.site.title' ~/Projects/AkurAI-Notes/site/backend/page.json \
  | grep -q "AkurAI-Notes" && echo "PASS: page.json has Notes title"

# Routes.json has the auth routes:
jq -r '.routes[].path' ~/Projects/AkurAI-Notes/site/backend/routes.json \
  | grep -q '/auth/callback' && echo "PASS: routes include auth/callback"

# Icon is square-ish and 512px:
identify ~/Projects/AkurAI-Notes/site/frontend/icon-notes.png 2>/dev/null \
  | grep -qE '512x512' && echo "PASS: icon is 512x512"
```

(If no `identify`, fall back to `file ~/Projects/AkurAI-Notes/site/frontend/icon-notes.png`
showing "PNG image data, 512 x 512".)

## Time

≈ 8 minutes (codex icon gen is the slow step — run it in the background; do the
HTML write while it runs).

## Depends on

E01 (workspace scaffold + bootstrapped `site/`).