diff --git a/CLAUDE.md b/CLAUDE.md index 8334000..2497aca 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,107 +1,89 @@ # CLAUDE.md -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +This file provides guidance to Claude Code when working with code in this repository. ## Project Overview -This is a static marketing website for Quantum Tasks AI, built with HTML/CSS/JavaScript and deployed using Docker/nginx. The site is containerized for deployment via Dokploy with Traefik reverse proxy handling SSL and routing. +Static marketing website for Quantum Tasks AI, served via nginx in Docker and deployed via Dokploy with Traefik for SSL and routing. -## Architecture +## File Structure -### Core Structure -- `index.html` - Main homepage with hero section, company profile, services overview, and integrated contact form -- `digital-branding.html` - AI Digital Branding services page with SOSTAC+RACE framework -- `css/style.css` - Consolidated stylesheet with responsive design and CSS custom properties -- `js/script.js` - Interactive functionality for forms, navigation, and animations - -### Deployment Architecture -- **Static Site**: Served via nginx in Docker container -- **Container**: Alpine-based nginx image with optimized caching headers -- **Orchestration**: Docker Compose with Traefik labels for automatic SSL/routing -- **Platform**: Dokploy deployment with domains `quantumtaskai.com` and `www.quantumtaskai.com` - -## Development Commands - -This is a static website with no build process - files are served directly by nginx. - -### Local Development -```bash -# Serve locally with any static server -python -m http.server 8000 -# or -npx serve . +``` +index.html — Homepage (dark navy hero, services, founders, contact) +digital-branding.html — AI Digital Branding services page (SOSTAC+RACE) +privacy.html — Privacy Policy +terms.html — Terms of Service +404.html — Custom 404 page +header.js — Shared self-injecting header (loaded cross-domain) +robots.txt — Search engine crawl instructions +sitemap.xml — XML sitemap (4 pages) +Dockerfile — nginx:alpine image +docker-compose.yml — Production config with Traefik labels +nginx.conf — nginx routing, caching headers, security headers, 404 +img/ — logo.png, favicon.ico, favicon-*.png, apple-touch-icon.png, og-image.png ``` -### Docker Development +## Shared Header + +`header.js` is a self-injecting IIFE loaded by all Quantum Tasks AI apps: +- Static site: ` - -
- {% block content %}{% endblock %} -
- - {% block extra_scripts %}{% endblock %} - - diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..cfc7849 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,23 @@ +version: "3.8" + +services: + web: + build: . + restart: unless-stopped + labels: + - "traefik.enable=true" + - "traefik.http.routers.quantumweb.rule=Host(`quantumtaskai.com`) || Host(`www.quantumtaskai.com`)" + - "traefik.http.routers.quantumweb.entrypoints=websecure" + - "traefik.http.routers.quantumweb.tls.certresolver=letsencrypt" + - "traefik.http.services.quantumweb.loadbalancer.server.port=80" + # www → non-www redirect + - "traefik.http.middlewares.www-redirect.redirectregex.regex=^https://www\\.quantumtaskai\\.com/(.*)" + - "traefik.http.middlewares.www-redirect.redirectregex.replacement=https://quantumtaskai.com/$${1}" + - "traefik.http.middlewares.www-redirect.redirectregex.permanent=true" + - "traefik.http.routers.quantumweb.middlewares=www-redirect" + networks: + - traefik-public + +networks: + traefik-public: + external: true diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..1e885ce --- /dev/null +++ b/nginx.conf @@ -0,0 +1,57 @@ +server { + listen 80; + server_name quantumtaskai.com www.quantumtaskai.com; + root /usr/share/nginx/html; + index index.html; + charset utf-8; + + # ── Security headers ───────────────────────────────────────── + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always; + + # ── Gzip compression ───────────────────────────────────────── + gzip on; + gzip_types text/plain text/css text/javascript application/javascript + application/json image/svg+xml; + gzip_min_length 1024; + + # ── Caching: HTML (1 hour) ─────────────────────────────────── + location ~* \.html$ { + add_header Cache-Control "public, max-age=3600, must-revalidate"; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + try_files $uri $uri/ =404; + } + + # ── Caching: Static assets (1 year) ───────────────────────── + location ~* \.(ico|png|jpg|jpeg|webp|gif|svg|woff|woff2|ttf|js|css)$ { + add_header Cache-Control "public, max-age=31536000, immutable"; + access_log off; + try_files $uri =404; + } + + # ── Caching: sitemap and robots (1 day) ───────────────────── + location ~* \.(xml|txt)$ { + add_header Cache-Control "public, max-age=86400"; + try_files $uri =404; + } + + # ── www → non-www redirect ─────────────────────────────────── + if ($host = "www.quantumtaskai.com") { + return 301 https://quantumtaskai.com$request_uri; + } + + # ── Main routing ───────────────────────────────────────────── + location / { + try_files $uri $uri/ $uri.html =404; + } + + # ── 404 page ───────────────────────────────────────────────── + error_page 404 /404.html; + location = /404.html { + internal; + } +} diff --git a/react-SharedHeader.jsx b/react-SharedHeader.jsx deleted file mode 100644 index ea5eb14..0000000 --- a/react-SharedHeader.jsx +++ /dev/null @@ -1,45 +0,0 @@ -import { useEffect } from 'react'; - -/** - * SharedHeader - * - * Dynamically loads the shared header script from your static site. - * Renders nothing itself — the script injects the header into . - * - * Usage: - * import SharedHeader from '@/components/SharedHeader'; - * // Place it once in your root layout: - * - * - * To bust cache after updating header.js, change the version param: - * const HEADER_URL = 'https://quantumtaskai.com/header.js?v=2'; - */ - -const HEADER_URL = 'https://quantumtaskai.com/header.js'; - -export default function SharedHeader() { - useEffect(() => { - // Avoid loading twice (e.g. StrictMode double-invoke) - if (document.getElementById('sh-header')) return; - if (document.querySelector(`script[src="${HEADER_URL}"]`)) return; - - const script = document.createElement('script'); - script.src = HEADER_URL; - script.async = true; - document.body.appendChild(script); - - return () => { - // Clean up on unmount - const injectedScript = document.querySelector(`script[src="${HEADER_URL}"]`); - if (injectedScript) injectedScript.remove(); - - const injectedHeader = document.getElementById('sh-header'); - if (injectedHeader) injectedHeader.remove(); - - const injectedStyles = document.getElementById('sh-styles'); - if (injectedStyles) injectedStyles.remove(); - }; - }, []); - - return null; -} diff --git a/react-layout-example.jsx b/react-layout-example.jsx deleted file mode 100644 index 2baf613..0000000 --- a/react-layout-example.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import SharedHeader from '@/components/SharedHeader'; - -/** - * Root layout for Next.js (works for both app/ and pages/ router). - * - * app/ router: use this as app/layout.jsx - * pages/ router: use this as pages/_app.jsx, wrapping - * - * The padding-top on
prevents page content from being hidden - * behind the sticky header (64px desktop / 56px mobile). - */ -export default function RootLayout({ children }) { - return ( - - - {/* Injects the shared header into via header.js */} - - -
- {children} -
- - - ); -} diff --git a/shared-header-README.md b/shared-header-README.md deleted file mode 100644 index 5d0441a..0000000 --- a/shared-header-README.md +++ /dev/null @@ -1,158 +0,0 @@ -# Shared Header System - -One header file. Three apps. Zero duplication. - -## How It Works - -``` -header.js (hosted on your main static site) - │ - ├── Static HTML site → -``` - -See `static-site-example.html` for a full working example. - ---- - -## Step 3 — Django Setup - -In your `base.html` template, add after `{% load static %}`: - -```html - -``` - -All templates that extend `base.html` automatically get the header. -See `django-base.html` for a full base template example. - ---- - -## Step 4 — React / Next.js Setup - -**1. Copy the component** to your project: -``` -src/components/SharedHeader.jsx (from react-SharedHeader.jsx) -``` - -**2. Add it to your root layout** (from `react-layout-example.jsx`): -```jsx -import SharedHeader from '@/components/SharedHeader'; - -export default function RootLayout({ children }) { - return ( - - - -
- {children} -
- - - ); -} -``` - ---- - -## Updating Nav Links - -Edit the `NAV_LINKS` array at the top of `header.js`: - -```js -var NAV_LINKS = [ - { label: 'Home', url: 'https://yoursite.com' }, - { label: 'About', url: 'https://yoursite.com/about' }, - // add or remove items here -]; -``` - -All connected apps reflect the change on next page load. No deploys needed for Django or React — they load the script remotely. - ---- - -## Updating Colors - -Edit the CSS variables near the top of `header.js`: - -```css -:root { - --sh-bg: #0a0e1a; /* background */ - --sh-accent: #3b82f6; /* accent / active color */ - --sh-text: #ffffff; /* nav link text */ -} -``` - ---- - -## Adding a New App - -Any app — PHP, Ruby, Vue, plain HTML — needs exactly one line: - -```html - -``` - ---- - -## Cache Busting After Updates - -When you update `header.js`, browsers may serve the old cached version. -Append a version query param to force a fresh fetch: - -```html - - -``` - -Update this in all three places (static HTML, Django base template, `SharedHeader.jsx`). - ---- - -## Troubleshooting - -**Header not showing?** -- Open browser DevTools → Network tab → check if `header.js` loaded (200 status). -- Check the console for errors. - -**CORS error in Django or React?** -- Your static site's server must allow cross-origin requests for JS files. -- In nginx, add to your server block: - ```nginx - add_header Access-Control-Allow-Origin "*"; - ``` - -**Content flashes before header appears?** -- Add `min-height: 64px` to a placeholder div before the script tag, or load the script in `` with `defer`. - -**z-index conflict (header appears behind other elements)?** -- The header uses `z-index: 99999`. If something still overlaps it, check for `z-index` or `transform` on a parent element (transforms create new stacking contexts). - -**Double header on React hot reload?** -- `header.js` checks `if (document.getElementById('sh-header')) return;` — this prevents double injection automatically. diff --git a/static-site-example.html b/static-site-example.html deleted file mode 100644 index 327191b..0000000 --- a/static-site-example.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - Page Title - YourSite - - - - - - - -
-

Welcome to YourSite

-

The header above is injected automatically by header.js.

-

To use on any page, add one script tag:

-
-<script src="https://quantumtaskai.com/header.js"></script>
-

That's it. No extra HTML, no CSS imports, no dependencies.

-
- - -