# Squiggleverse Landing Page Design Document / Spec Sheet

## 1. Project Summary

**Project:** Squiggleverse landing page  
**Primary goal:** Present Squiggleverse as a whimsical, polished, education-through-play platform using a simple, high-performing homepage structure.

**Recommended layout:**

1. Logo / hero branding
2. Embedded or self-hosted promo video
3. Short explanatory text
4. Optional CTA row: “Watch Demo,” “Join Mailing List,” “Contact Us”

**Core positioning text:**

> Squiggleverse is a modular, multi-platform, game-based “stealth” learning system that teaches grammar, usage, and style through innovative gameplay, whimsical characters, and creative narratives co-authored by the player.

The page should feel like a lightweight marketing splash page rather than a heavy SaaS homepage.

---

## 2. Recommended Web Stack

### Framework

Use **SvelteKit** as the primary framework.

SvelteKit is appropriate here because the landing page can be statically rendered, very fast, SEO-friendly, and simple to deploy. SvelteKit supports prerendering/static generation and server-side rendering options per route, which is ideal for a marketing page that may later grow into a larger app or dashboard.

### Suggested stack

```txt
SvelteKit
TypeScript
Vite
CSS / SCSS / PostCSS
Static adapter or platform adapter
PNPM
ESLint + Prettier
```

### Deployment targets

Good options:

```txt
Cloudflare Pages
Netlify
Vercel
Static hosting behind nginx/Caddy
CapRover container deployment
```

For this page, **static generation is preferred** unless the site needs dynamic personalization, server-side forms, authentication, or CMS previews.

Recommended SvelteKit page config:

```ts
// src/routes/+page.ts
export const prerender = true;
export const ssr = true;
```

For a landing page, keep SSR/prerendering enabled so the logo, text, metadata, and canonical content are present in the initial HTML.

---

## 3. Page Structure

### Route

```txt
/
```

### File layout

```txt
src/
  lib/
    assets/
      squiggleverse-logo.webp
      squiggleverse-bg.webp
      squiggleverse-poster.webp
      squiggleverse-demo.mp4
    components/
      HeroLogo.svelte
      VideoCard.svelte
      IntroText.svelte
      CTAButtons.svelte
      StarBackground.svelte
  routes/
    +layout.svelte
    +layout.ts
    +page.svelte
    +page.ts
    +error.svelte
  app.html
  app.css
static/
  favicon.svg
  robots.txt
  site.webmanifest
  og-image.webp
```

---

## 4. Visual Design Direction

### Overall style

The landing page should use:

```txt
Whimsical
Space-themed
Purple/violet palette
Soft glow effects
Rounded cards
Centered single-column layout
Large readable text
Minimal navigation
```

### Layout wireframe

```txt
┌─────────────────────────────────────┐
│                                     │
│              Logo                   │
│                                     │
│        ┌───────────────────┐        │
│        │                   │        │
│        │      Video        │        │
│        │                   │        │
│        └───────────────────┘        │
│                                     │
│        Short description text       │
│                                     │
│      [ Watch Demo ] [ Contact ]     │
│                                     │
└─────────────────────────────────────┘
```

### Responsive behavior

#### Desktop

```txt
Max content width: 960–1100px
Logo width: 520–720px
Video width: 800–960px
Text width: 680–820px
```

#### Tablet

```txt
Content width: 90vw
Logo width: 70–80vw
Video width: 90vw
Text width: 80–90vw
```

#### Mobile

```txt
Single column
Logo width: 86vw
Video width: 92vw
Text centered or slightly left-aligned if readability suffers
Font size reduced carefully
```

---

## 5. Color System

Use a constrained purple palette.

```css
:root {
  --color-bg-deep: #070014;
  --color-bg-space: #160030;
  --color-purple-dark: #2a0f56;
  --color-purple: #6b3fb3;
  --color-purple-light: #b58cff;
  --color-lavender: #f1e7ff;
  --color-white: #ffffff;
  --color-cyan-accent: #43d8ff;

  --glow-purple: 0 0 32px rgba(159, 95, 255, 0.45);
  --border-purple: rgba(190, 143, 255, 0.38);
}
```

### Background

Use the provided starry background as a fullscreen visual layer.

Recommended CSS:

```css
.page {
  min-height: 100svh;
  background:
    linear-gradient(
      to bottom,
      rgba(7, 0, 20, 0.2),
      rgba(54, 0, 95, 0.35)
    ),
    url('/assets/squiggleverse-bg.webp');
  background-size: cover;
  background-position: center top;
  background-attachment: fixed;
}
```

For mobile, avoid `background-attachment: fixed` because it can cause jank.

```css
@media (max-width: 768px) {
  .page {
    background-attachment: scroll;
  }
}
```

---

## 6. Typography

### Primary recommendation

Use a highly readable sans-serif for body copy.

Good options:

```txt
Atkinson Hyperlegible
Nunito Sans
Inter
Lexend
Source Sans 3
```

For an educational product, **Atkinson Hyperlegible** or **Lexend** would fit well because they emphasize readability.

### Font loading

Prefer self-hosted fonts using `@font-face`.

```css
@font-face {
  font-family: 'Atkinson Hyperlegible';
  src: url('/fonts/AtkinsonHyperlegible-Regular.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}
```

Use `font-display: swap` to avoid invisible text during load.

---

## 7. Component Specification

### `HeroLogo.svelte`

Purpose:

```txt
Displays the Squiggleverse logo centered at the top.
```

Requirements:

```txt
Use actual provided logo asset.
Do not recreate the logo in text.
Use responsive sizing.
Include alt text.
Avoid layout shift by declaring width/height or aspect ratio.
```

Example:

```svelte
<script lang="ts">
  import logo from '$lib/assets/squiggleverse-logo.webp';
</script>

<div class="hero-logo">
  <img
    src={logo}
    alt="Squiggleverse"
    width="900"
    height="420"
    decoding="async"
  />
</div>
```

CSS:

```css
.hero-logo {
  display: flex;
  justify-content: center;
  margin-inline: auto;
}

.hero-logo img {
  width: min(720px, 88vw);
  height: auto;
  filter: drop-shadow(0 0 28px rgba(159, 95, 255, 0.45));
}
```

---

### `VideoCard.svelte`

Purpose:

```txt
Displays the main Squiggleverse video or poster/video preview.
```

Preferred implementation:

```txt
Use native <video> for self-hosted promo video.
Use poster image.
Lazy-load if below fold, but in this layout it is likely above fold.
Avoid YouTube iframe unless necessary.
```

Native video gives better branding control, fewer third-party scripts, and better privacy/performance.

Example:

```svelte
<script lang="ts">
  import poster from '$lib/assets/squiggleverse-poster.webp';
  import video from '$lib/assets/squiggleverse-demo.mp4';
</script>

<section class="video-card" aria-label="Squiggleverse demo video">
  <video
    controls
    preload="metadata"
    poster={poster}
    playsinline
  >
    <source src={video} type="video/mp4" />
    Your browser does not support the video tag.
  </video>
</section>
```

CSS:

```css
.video-card {
  width: min(940px, 92vw);
  margin: 2rem auto 2.25rem;
  padding: clamp(0.5rem, 1.2vw, 0.9rem);
  border: 1px solid var(--border-purple);
  border-radius: 2rem;
  background:
    linear-gradient(
      180deg,
      rgba(28, 10, 72, 0.72),
      rgba(12, 4, 38, 0.78)
    );
  box-shadow:
    var(--glow-purple),
    inset 0 0 40px rgba(255, 255, 255, 0.04);
  backdrop-filter: blur(8px);
}

.video-card video {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: 1.45rem;
  background: #090019;
}
```

### Video asset requirements

```txt
Format: MP4/H.264 baseline or high profile
Optional: WebM VP9/AV1 for modern browsers
Poster: WebP or AVIF
Resolution: 1280×720 minimum
Max video size target: 3–8 MB for landing page
Audio: compressed AAC, normalized
Captions: WebVTT file if voiceover exists
```

Add captions if there is spoken content:

```svelte
<track
  kind="captions"
  src="/captions/squiggleverse-demo.vtt"
  srclang="en"
  label="English"
/>
```

---

### `IntroText.svelte`

Purpose:

```txt
Displays the main product positioning copy.
```

Requirements:

```txt
Exact text preserved.
Readable line length.
High contrast.
No image-rendered text.
```

Example:

```svelte
<section class="intro" aria-labelledby="intro-title">
  <h1 id="intro-title" class="sr-only">About Squiggleverse</h1>

  <p>
    Squiggleverse is a modular, multi-platform, game-based “stealth” learning
    system that teaches grammar, usage, and style through innovative gameplay,
    whimsical characters, and creative narratives co-authored by the player.
  </p>
</section>
```

CSS:

```css
.intro {
  width: min(820px, 88vw);
  margin: 0 auto;
  text-align: center;
}

.intro p {
  margin: 0;
  color: var(--color-lavender);
  font-size: clamp(1.2rem, 2.2vw, 2rem);
  line-height: 1.45;
  letter-spacing: 0.01em;
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.45);
}
```

---

### Optional `CTAButtons.svelte`

Even if the initial page is simple, adding a CTA is useful for conversion.

Recommended CTAs:

```txt
Primary: Watch Demo
Secondary: Contact Us
Optional: Join Mailing List
```

Example:

```svelte
<div class="cta-row">
  <a class="button button-primary" href="#demo">Watch Demo</a>
  <a class="button button-secondary" href="mailto:hello@squiggleverse.com">
    Contact Us
  </a>
</div>
```

---

## 8. Accessibility Requirements

### Required

```txt
Logo must have meaningful alt text.
Video must have controls.
Video needs captions if it contains speech.
Text must not be baked into images.
All interactive elements need visible focus states.
Color contrast should meet WCAG AA.
Animations/glows should respect prefers-reduced-motion.
Page must be keyboard navigable.
```

### Reduced motion

```css
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
}
```

### Focus styling

```css
:focus-visible {
  outline: 3px solid var(--color-cyan-accent);
  outline-offset: 4px;
}
```

---

## 9. SEO Specification

### Rendering

Use prerendered HTML for the homepage. Search engines should receive real text content, not an empty SPA shell. SvelteKit can prerender static routes into static files, which fits this use case well.

### Metadata

Implement in `+page.svelte`:

```svelte
<svelte:head>
  <title>Squiggleverse | Game-Based Grammar, Usage, and Style Learning</title>
  <meta
    name="description"
    content="Squiggleverse is a modular, multi-platform, game-based stealth learning system for grammar, usage, and style."
  />
  <link rel="canonical" href="https://squiggleverse.com/" />

  <meta property="og:type" content="website" />
  <meta property="og:title" content="Squiggleverse" />
  <meta
    property="og:description"
    content="A whimsical game-based learning system for grammar, usage, and style."
  />
  <meta property="og:image" content="https://squiggleverse.com/og-image.webp" />
  <meta property="og:url" content="https://squiggleverse.com/" />

  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:title" content="Squiggleverse" />
  <meta
    name="twitter:description"
    content="A whimsical game-based learning system for grammar, usage, and style."
  />
  <meta name="twitter:image" content="https://squiggleverse.com/og-image.webp" />
</svelte:head>
```

### Suggested title variants

```txt
Squiggleverse | Game-Based Grammar Learning
Squiggleverse | Learn Grammar Through Play
Squiggleverse | Stealth Learning Through Whimsical Gameplay
```

### Suggested meta description

```txt
Squiggleverse is a modular, multi-platform, game-based stealth learning system that teaches grammar, usage, and style through whimsical gameplay.
```

Keep meta descriptions around 140–160 characters when possible.

### Heading structure

Recommended:

```html
<h1>Squiggleverse</h1>
```

Because the logo is graphical, include a visually hidden H1.

```svelte
<h1 class="sr-only">Squiggleverse</h1>
```

Use only one H1 on the page.

### Structured data

Add basic `WebSite` and `Organization` JSON-LD.

```svelte
<svelte:head>
  <script type="application/ld+json">
    {JSON.stringify({
      '@context': 'https://schema.org',
      '@type': 'WebSite',
      name: 'Squiggleverse',
      url: 'https://squiggleverse.com/',
      description:
        'A modular, multi-platform, game-based stealth learning system for grammar, usage, and style.'
    })}
  </script>
</svelte:head>
```

If a company/entity name exists, add `Organization`.

### Robots

`static/robots.txt`:

```txt
User-agent: *
Allow: /

Sitemap: https://squiggleverse.com/sitemap.xml
```

### Sitemap

For one page, the sitemap is simple:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="https://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://squiggleverse.com/</loc>
    <changefreq>monthly</changefreq>
    <priority>1.0</priority>
  </url>
</urlset>
```

---

## 10. Performance Requirements

### Core Web Vitals targets

Use these targets:

```txt
LCP: ≤ 2.5s
CLS: ≤ 0.1
INP: ≤ 200ms
Total JS: ideally under 100 KB compressed for this page
Initial image payload: ideally under 500 KB before video
Video: do not autoplay large media
```

### LCP optimization

Likely LCP candidate:

```txt
Logo image
or
intro text block
or
video poster image
```

Recommendations:

```txt
Use optimized WebP/AVIF.
Declare image dimensions.
Preload the logo only if it is the true LCP element.
Avoid client-only rendering for hero content.
Do not load heavy JS before first paint.
```

Example preload:

```svelte
<svelte:head>
  <link
    rel="preload"
    as="image"
    href="/assets/squiggleverse-logo.webp"
    type="image/webp"
  />
</svelte:head>
```

Only preload the logo if testing confirms it improves LCP.

### CLS prevention

```txt
Set width/height on images.
Use aspect-ratio for video card.
Avoid late-loading web fonts without fallback sizing.
Reserve space for video controls/poster.
Avoid injecting CTA/forms above existing content after hydration.
```

### Video performance

Use:

```html
preload="metadata"
poster="/assets/squiggleverse-poster.webp"
playsinline
controls
```

Avoid:

```html
autoplay
preload="auto"
large uncompressed MOV files
third-party iframe on first load
```

If YouTube/Vimeo is required, use a “lite embed” strategy:

```txt
Show poster image first.
Load iframe only after click.
```

### Image optimization

Generate:

```txt
logo.webp
logo.avif
background.webp
background.avif
poster.webp
poster.avif
og-image.webp
```

Recommended sizes:

```txt
Logo: 900–1200 px wide
Background: 1920 px wide desktop, 960 px mobile variant
Poster: 1280×720
OG image: 1200×630
```

Use `srcset` for the background or use CSS media queries.

---

## 11. Security Specification

### Content Security Policy

SvelteKit supports CSP configuration that helps restrict where scripts, styles, images, fonts, and other resources can load from. CSP is specifically useful for reducing XSS exposure.

Example starter CSP in `svelte.config.js`:

```js
const config = {
  kit: {
    csp: {
      mode: 'auto',
      directives: {
        'default-src': ['self'],
        'script-src': ['self'],
        'style-src': ['self', 'unsafe-inline'],
        'img-src': ['self', 'data:'],
        'font-src': ['self'],
        'media-src': ['self'],
        'connect-src': ['self'],
        'frame-src': ['self'],
        'object-src': ['none'],
        'base-uri': ['self'],
        'form-action': ['self'],
        'frame-ancestors': ['none']
      }
    }
  }
};

export default config;
```

Notes:

```txt
Avoid unsafe-inline for scripts.
Style unsafe-inline may be necessary depending on SvelteKit output and deployment mode.
If using YouTube/Vimeo, frame-src and img-src need additional domains.
If using analytics, connect-src/script-src must include that provider.
```

### Security headers

Recommended headers:

```txt
Content-Security-Policy
Strict-Transport-Security
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy
X-Frame-Options: DENY or CSP frame-ancestors 'none'
Cross-Origin-Opener-Policy: same-origin
```

Example SvelteKit hook:

```ts
// src/hooks.server.ts
import type { Handle } from '@sveltejs/kit';

export const handle: Handle = async ({ event, resolve }) => {
  const response = await resolve(event);

  response.headers.set('X-Content-Type-Options', 'nosniff');
  response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
  response.headers.set(
    'Permissions-Policy',
    'camera=(), microphone=(), geolocation=(), payment=()'
  );

  return response;
};
```

For fully static deployment, set these headers at the host/CDN level instead.

### Forms

If adding a mailing list form:

```txt
Use server-side validation.
Add rate limiting.
Use CSRF protection where applicable.
Never expose API secrets in client-side env vars.
Use honeypot field or Turnstile/hCaptcha if spam becomes a problem.
```

### Environment variables

Use:

```txt
.env
PRIVATE_*
PUBLIC_*
```

Do not put API secrets in `PUBLIC_*`.

---

## 12. Privacy Considerations

Recommended:

```txt
Avoid third-party trackers initially.
Avoid third-party video if possible.
Self-host fonts.
Self-host video.
Self-host images.
Use privacy-friendly analytics only if needed.
```

Good analytics options:

```txt
Plausible
Fathom
Umami self-hosted
Cloudflare Web Analytics
No analytics for MVP
```

If analytics are added, update:

```txt
Privacy policy
Cookie notice if cookies are used
CSP domains
```

---

## 13. SvelteKit Implementation Sketch

### `src/routes/+page.svelte`

```svelte
<script lang="ts">
  import HeroLogo from '$lib/components/HeroLogo.svelte';
  import VideoCard from '$lib/components/VideoCard.svelte';
  import IntroText from '$lib/components/IntroText.svelte';
  import CTAButtons from '$lib/components/CTAButtons.svelte';
</script>

<svelte:head>
  <title>Squiggleverse | Game-Based Grammar, Usage, and Style Learning</title>
  <meta
    name="description"
    content="Squiggleverse is a modular, multi-platform, game-based stealth learning system that teaches grammar, usage, and style through whimsical gameplay."
  />
  <link rel="canonical" href="https://squiggleverse.com/" />
</svelte:head>

<main class="landing-page">
  <h1 class="sr-only">Squiggleverse</h1>

  <section class="hero" aria-label="Squiggleverse landing page">
    <HeroLogo />
    <VideoCard />
    <IntroText />
    <CTAButtons />
  </section>
</main>
```

### `src/app.css`

```css
:root {
  color-scheme: dark;

  --color-bg-deep: #070014;
  --color-bg-space: #160030;
  --color-purple-dark: #2a0f56;
  --color-purple: #6b3fb3;
  --color-purple-light: #b58cff;
  --color-lavender: #f1e7ff;
  --color-white: #ffffff;
  --color-cyan-accent: #43d8ff;

  --glow-purple: 0 0 32px rgba(159, 95, 255, 0.45);
  --border-purple: rgba(190, 143, 255, 0.38);

  font-family:
    'Atkinson Hyperlegible',
    system-ui,
    -apple-system,
    BlinkMacSystemFont,
    'Segoe UI',
    sans-serif;
}

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  min-width: 320px;
  background: var(--color-bg-deep);
  color: var(--color-white);
}

img,
video {
  max-width: 100%;
}

.landing-page {
  min-height: 100svh;
  background:
    radial-gradient(circle at top, rgba(123, 69, 202, 0.35), transparent 45%),
    linear-gradient(to bottom, rgba(7, 0, 20, 0.4), rgba(54, 0, 95, 0.45)),
    url('/assets/squiggleverse-bg.webp');
  background-size: cover;
  background-position: center top;
}

.hero {
  width: min(1120px, 100%);
  margin-inline: auto;
  padding: clamp(2rem, 5vw, 4.5rem) 1rem clamp(3rem, 7vw, 6rem);
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

:focus-visible {
  outline: 3px solid var(--color-cyan-accent);
  outline-offset: 4px;
}

@media (max-width: 768px) {
  .landing-page {
    background-attachment: scroll;
  }
}
```

---

## 14. Build and Tooling

### Package setup

```bash
pnpm create svelte@latest squiggleverse-landing
cd squiggleverse-landing
pnpm install
pnpm dev
```

### Recommended dependencies

```bash
pnpm add -D prettier eslint typescript svelte-check vite
```

Optional:

```bash
pnpm add -D @sveltejs/adapter-static
```

### Static adapter config

```js
// svelte.config.js
import adapter from '@sveltejs/adapter-static';

const config = {
  kit: {
    adapter: adapter({
      pages: 'build',
      assets: 'build',
      fallback: undefined,
      precompress: true,
      strict: true
    })
  }
};

export default config;
```

### Quality checks

```bash
pnpm check
pnpm lint
pnpm build
pnpm preview
```

---

## 15. Performance Budget

Recommended launch budget:

```txt
HTML: < 30 KB
CSS: < 50 KB compressed
JS: < 100 KB compressed
Logo: < 200 KB
Background: < 300 KB desktop, < 150 KB mobile
Poster: < 250 KB
Video: < 8 MB
Third-party scripts: 0 initially
Fonts: < 150 KB total
```

### Testing tools

```txt
Lighthouse
PageSpeed Insights
WebPageTest
Chrome DevTools Performance tab
SvelteKit build output
```

---

## 16. Browser Support

Target:

```txt
Latest Chrome
Latest Firefox
Latest Safari
Latest Edge
iOS Safari
Android Chrome
```

Graceful degradation:

```txt
No backdrop-filter required for usability.
No autoplay dependency.
No JavaScript dependency for reading core page content.
```

---

## 17. Content Requirements

### Required homepage copy

Use exact copy unless changed by brand team:

```txt
Squiggleverse is a modular, multi-platform, game-based “stealth” learning system that teaches grammar, usage, and style through innovative gameplay, whimsical characters, and creative narratives co-authored by the player.
```

### Optional additional copy

Use only if the page needs more conversion support:

```txt
Learn by playing.
Build stories with whimsical characters.
Practice grammar, usage, and style without worksheets.
Designed for modular, multi-platform deployment.
```

---

## 18. Launch Checklist

### Visual

```txt
Logo displays crisply on desktop and mobile.
Video card scales correctly.
Background does not overpower text.
Text remains readable at all breakpoints.
CTA buttons, if used, are obvious but not visually noisy.
```

### SEO

```txt
Unique title.
Meta description.
Canonical URL.
Open Graph tags.
Twitter card tags.
Robots.txt.
Sitemap.xml.
Visible/indexable body text.
Single H1.
No accidental noindex.
```

### Performance

```txt
Images compressed.
Video uses poster and preload=metadata.
No unnecessary JS libraries.
No large animation library.
No layout shift.
Passes Lighthouse performance target.
```

### Accessibility

```txt
Keyboard navigable.
Visible focus styles.
Video controls available.
Captions if voiceover exists.
Color contrast passes.
Reduced-motion support.
No text-only information inside images.
```

### Security

```txt
HTTPS enforced.
CSP configured.
Security headers configured.
No exposed secrets.
No unnecessary third-party scripts.
Forms validated server-side if present.
```

---

## 19. Recommended MVP Scope

For the first version, build:

```txt
Static SvelteKit landing page
Logo hero
Self-hosted video with poster
Main descriptive paragraph
One CTA: Contact Us or Watch Demo
SEO metadata
OG image
robots.txt
sitemap.xml
Basic security headers
Optimized image/video assets
```

Avoid for MVP:

```txt
CMS
Animation framework
User accounts
Heavy analytics
YouTube embed
Complex routing
Newsletter provider scripts loaded on first paint
```

The best implementation is a prerendered SvelteKit page with the brand assets self-hosted, minimal JavaScript, real HTML text, and a tightly optimized media pipeline.
