feat: scaffold Astro project with layout, nav, hero and concept sections

- Add global ocean-themed styles (Inter + Sora, glassmorphism utilities).
- Wire up base layout, sticky nav and footer respecting base path.
- Build hero with stylised rock SVG + sonar ambient rings and concept pipeline schematic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-21 07:27:31 +00:00
parent ea461629f3
commit aeec79d7b4
14 changed files with 7369 additions and 0 deletions

10
.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
node_modules/
dist/
.astro/
.DS_Store
*.log
.env
.env.local
.env.*.local
.vscode/
.idea/

18
astro.config.mjs Normal file
View File

@@ -0,0 +1,18 @@
import { defineConfig } from 'astro/config';
import tailwind from '@astrojs/tailwind';
export default defineConfig({
site: 'https://laboratoire.freeboxos.fr',
base: '/nowyousea/',
output: 'static',
trailingSlash: 'ignore',
integrations: [tailwind({ applyBaseStyles: false })],
build: {
inlineStylesheets: 'auto',
},
vite: {
build: {
cssCodeSplit: true,
},
},
});

6650
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

18
package.json Normal file
View File

@@ -0,0 +1,18 @@
{
"name": "site-nowyousea",
"type": "module",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"astro": "^5.15.0",
"@astrojs/tailwind": "^6.0.2",
"tailwindcss": "^3.4.17"
}
}

14
public/favicon.svg Normal file
View File

@@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<defs>
<linearGradient id="g" x1="0" x2="0" y1="0" y2="1">
<stop offset="0" stop-color="#38bdf8"/>
<stop offset="1" stop-color="#06b6d4"/>
</linearGradient>
</defs>
<rect width="64" height="64" rx="14" fill="#050d1a"/>
<path d="M6 40c6 0 6-6 12-6s6 6 12 6 6-6 12-6 6 6 12 6 6-6 10-6v14H6z" fill="url(#g)" opacity="0.55"/>
<path d="M6 46c6 0 6-6 12-6s6 6 12 6 6-6 12-6 6 6 12 6 6-6 10-6v8H6z" fill="url(#g)"/>
<circle cx="32" cy="22" r="4" fill="#e0f2fe"/>
<circle cx="32" cy="22" r="9" fill="none" stroke="#38bdf8" stroke-width="1.5" opacity="0.6"/>
<circle cx="32" cy="22" r="14" fill="none" stroke="#38bdf8" stroke-width="1" opacity="0.3"/>
</svg>

After

Width:  |  Height:  |  Size: 747 B

3
run-build.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
cd /home/floppyrj45/Projects/site-nowyousea
exec claude --dangerously-skip-permissions --print "Lis BRIEF.md intégralement puis exécute toutes les étapes. Le contenu source est dans content/. Build un site Astro SSG vitrine NowYouSea avec base: '/nowyousea/', design océan moderne (bleus nuit + cyan accent), sections Hero, Concept, Pourquoi, 4 configurations (MINI/STD/PRO/EOLIEN), Architecture système, Cas d'usage Port-Navalo, Équipe (Baptiste Moulin CTO, Quentin Ingé Produit, Radu Mihail Business EU), Roadmap, Contact. Interdiction absolue de mentionner Octo'pousse, candidature, concours, IFREMER-jury. Livre: projet Astro complet + npm run build OK + DEPLOY.md avec commandes rsync vers floppyrj45@192.168.0.82:/srv/www/nowyousea/ + snippet Caddy. Commits git au fil de l'eau. Tu as le droit de tout faire (bypass permissions), pas besoin de demander confirmation. Quand le build dist/ est produit, termine en lançant: openclaw system event --text 'Done: site vitrine NowYouSea buildé dans ~/Projects/site-nowyousea/dist' --mode now" 2>&1

View File

@@ -0,0 +1,133 @@
---
const steps = [
{
title: 'Terrain',
text: 'Un rocher biomimétique immergé, discret, accueille hydrophone calibré, CTD et capteurs modulaires.',
icon: 'rock',
},
{
title: 'Calcul embarqué',
text: 'FFT et SPL calculés directement sur la station. On transmet lindicateur, pas le fichier brut.',
icon: 'cpu',
},
{
title: 'Cloud',
text: 'Collecte MQTT sécurisée, stockage time-series, moteur dalertes et inter-calibration.',
icon: 'cloud',
},
{
title: 'Dashboard',
text: 'Carte live, historique, rapports réglementaires auto-générés, API pour la recherche.',
icon: 'dashboard',
},
];
---
<section id="concept" class="section">
<div class="container-narrow">
<div class="section-title-block">
<span class="eyebrow">Concept</span>
<h2 class="h-section mt-4">
Une station côtière autonome, du capteur au tableau de bord.
</h2>
<p class="lead mt-6">
NowYouSea est un réseau de rochers connectés qui écoutent, mesurent et transmettent
en continu. L'intelligence des grandes stations de référence, à la portée d'un port —
une version moderne et démocratisée du MAREL, conçue pour le littoral.
</p>
</div>
<!-- Pipeline schematic -->
<div class="relative">
<!-- Connecting gradient line (desktop only) -->
<div class="pointer-events-none absolute left-0 right-0 top-[72px] hidden h-px md:block"
aria-hidden="true"
style="background: linear-gradient(90deg, transparent, rgba(56,189,248,0.4), rgba(6,182,212,0.55), rgba(56,189,248,0.4), transparent);"></div>
<ol class="grid gap-5 md:grid-cols-4">
{steps.map((step, i) => (
<li class="relative">
<div class="card h-full">
<div class="flex items-center gap-3 mb-4">
<span class="inline-flex h-10 w-10 items-center justify-center rounded-lg bg-tide-500/15 text-tide-400 ring-1 ring-tide-400/30">
{step.icon === 'rock' && (
<svg viewBox="0 0 24 24" class="h-5 w-5" fill="currentColor" aria-hidden="true"><path d="M4 17c0-4 3-6 5-6 1 0 2-3 5-3s4 2 4 4c1 0 2 1 2 3s-2 4-5 4H6c-1 0-2-1-2-2z"/></svg>
)}
{step.icon === 'cpu' && (
<svg viewBox="0 0 24 24" class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round" aria-hidden="true">
<rect x="6" y="6" width="12" height="12" rx="2"/>
<rect x="9" y="9" width="6" height="6" rx="1"/>
<path d="M4 9h2M4 13h2M18 9h2M18 13h2M9 4v2M13 4v2M9 18v2M13 18v2"/>
</svg>
)}
{step.icon === 'cloud' && (
<svg viewBox="0 0 24 24" class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M7 17h10a4 4 0 0 0 0-8 6 6 0 0 0-11.7 1.3A3.5 3.5 0 0 0 7 17z"/>
</svg>
)}
{step.icon === 'dashboard' && (
<svg viewBox="0 0 24 24" class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<rect x="3" y="4" width="18" height="16" rx="2"/>
<path d="M3 9h18M8 14h3M8 17h7"/>
</svg>
)}
</span>
<span class="font-display text-sm font-medium uppercase tracking-widest text-foam-100/50">
0{i + 1}
</span>
</div>
<h3 class="font-display text-xl font-semibold text-foam-50">{step.title}</h3>
<p class="mt-3 text-sm leading-relaxed text-foam-100/75">{step.text}</p>
</div>
{i < steps.length - 1 && (
<svg
viewBox="0 0 40 24"
class="pointer-events-none absolute -right-3 top-1/2 hidden h-6 w-10 -translate-y-1/2 text-tide-400/70 md:block"
fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"
>
<path d="M2 12h34M30 6l6 6-6 6"/>
</svg>
)}
</li>
))}
</ol>
</div>
<!-- Feature bullets -->
<div class="mt-16 grid gap-6 md:grid-cols-3">
<div class="flex items-start gap-4">
<span class="mt-1 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-lagoon-500/15 text-lagoon-400">
<svg viewBox="0 0 24 24" class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M20 6L9 17l-5-5"/></svg>
</span>
<div>
<h4 class="font-semibold text-foam-50">Biomimétique &amp; discret</h4>
<p class="mt-1 text-sm text-foam-100/70">
Forme de rocher qui se fond dans l'enrochement, sans encombrement maritime ni impact visuel.
</p>
</div>
</div>
<div class="flex items-start gap-4">
<span class="mt-1 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-lagoon-500/15 text-lagoon-400">
<svg viewBox="0 0 24 24" class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M20 6L9 17l-5-5"/></svg>
</span>
<div>
<h4 class="font-semibold text-foam-50">Monitoring-grade</h4>
<p class="mt-1 text-sm text-foam-100/70">
Précision réglementaire pour la conformité et les alertes, inter-calibrable avec les stations de référence.
</p>
</div>
</div>
<div class="flex items-start gap-4">
<span class="mt-1 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-lagoon-500/15 text-lagoon-400">
<svg viewBox="0 0 24 24" class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M20 6L9 17l-5-5"/></svg>
</span>
<div>
<h4 class="font-semibold text-foam-50">Open &amp; interopérable</h4>
<p class="mt-1 text-sm text-foam-100/70">
API REST, WebSocket, exports CSV et formats compatibles avec les référentiels publics.
</p>
</div>
</div>
</div>
</div>
</section>

View File

@@ -0,0 +1,30 @@
---
const year = new Date().getFullYear();
---
<footer class="relative border-t border-white/5 py-12 mt-12">
<div class="container-narrow flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
<div class="flex items-center gap-3">
<span class="inline-flex h-8 w-8 items-center justify-center rounded-md bg-gradient-to-br from-tide-500 to-lagoon-500">
<svg viewBox="0 0 24 24" class="h-5 w-5 text-abyss-950" fill="currentColor" aria-hidden="true">
<path d="M2 16c2 0 2-2 4-2s2 2 4 2 2-2 4-2 2 2 4 2 2-2 4-2v4H2z"/>
<circle cx="12" cy="9" r="2"/>
</svg>
</span>
<div>
<div class="font-display font-semibold">NowYouSea</div>
<div class="text-xs text-foam-100/60">Le rocher connecté qui écoute l'océan.</div>
</div>
</div>
<nav aria-label="Navigation pied de page" class="flex flex-wrap gap-x-6 gap-y-2 text-sm text-foam-100/60">
<a href="#concept" class="hover:text-tide-400">Concept</a>
<a href="#configurations" class="hover:text-tide-400">Configurations</a>
<a href="#architecture" class="hover:text-tide-400">Architecture</a>
<a href="#equipe" class="hover:text-tide-400">Équipe</a>
<a href="#contact" class="hover:text-tide-400">Contact</a>
</nav>
<div class="text-xs text-foam-100/50">© {year} NowYouSea — Tous droits réservés.</div>
</div>
</footer>

172
src/components/Hero.astro Normal file
View File

@@ -0,0 +1,172 @@
---
---
<section class="relative overflow-hidden pt-16 md:pt-24 pb-24 md:pb-32">
<!-- Ambient caustic glow -->
<div class="pointer-events-none absolute inset-0 caustic-bg"></div>
<!-- Decorative SVG backdrop: horizon + underwater layers -->
<svg
class="pointer-events-none absolute inset-x-0 top-0 h-[540px] w-full opacity-70"
viewBox="0 0 1440 540"
preserveAspectRatio="none"
aria-hidden="true"
>
<defs>
<linearGradient id="horizon" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#050d1a" stop-opacity="0"/>
<stop offset="1" stop-color="#0a1a2f" stop-opacity="0.85"/>
</linearGradient>
<linearGradient id="wave" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#0ea5e9" stop-opacity="0.18"/>
<stop offset="1" stop-color="#0a1a2f" stop-opacity="0"/>
</linearGradient>
</defs>
<rect width="1440" height="540" fill="url(#horizon)"/>
<path d="M0 380 C 240 340, 480 420, 720 380 S 1200 320, 1440 380 L1440 540 L0 540 Z" fill="url(#wave)"/>
<path d="M0 440 C 300 410, 500 460, 820 430 S 1220 400, 1440 440 L1440 540 L0 540 Z" fill="#0ea5e9" fill-opacity="0.06"/>
</svg>
<div class="container-narrow relative">
<div class="grid lg:grid-cols-12 gap-12 items-center">
<!-- Copy -->
<div class="lg:col-span-7 relative">
<span class="eyebrow">Station côtière · Acoustique sous-marine</span>
<h1 class="h-display mt-6">
Le rocher connecté
<span class="block bg-gradient-to-r from-foam-50 via-tide-400 to-lagoon-400 bg-clip-text text-transparent">
qui écoute l'océan.
</span>
</h1>
<p class="lead mt-7 max-w-xl">
NowYouSea déploie un réseau de stations côtières discrètes, autonomes
et abordables — hydrophone calibré, physico-chimie et calcul embarqué —
pour donner aux ports et aux gestionnaires d'espaces marins des données
continues, du terrain jusqu'au tableau de bord.
</p>
<div class="mt-10 flex flex-wrap items-center gap-3">
<a href="#concept" class="btn btn-primary">
Découvrir le projet
<svg viewBox="0 0 24 24" class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M12 5v14M5 12l7 7 7-7"/>
</svg>
</a>
<a href="#configurations" class="btn btn-ghost">Voir les configurations</a>
</div>
<!-- Key metrics strip -->
<dl class="mt-12 grid grid-cols-3 gap-4 max-w-xl">
<div class="glass rounded-xl p-4">
<dt class="text-xs uppercase tracking-wider text-foam-100/60">Hydrophone</dt>
<dd class="mt-1 font-display text-lg font-semibold text-tide-400">63 &amp; 125 Hz</dd>
</div>
<div class="glass rounded-xl p-4">
<dt class="text-xs uppercase tracking-wider text-foam-100/60">Autonomie</dt>
<dd class="mt-1 font-display text-lg font-semibold text-tide-400">Solaire 24/7</dd>
</div>
<div class="glass rounded-xl p-4">
<dt class="text-xs uppercase tracking-wider text-foam-100/60">Transmission</dt>
<dd class="mt-1 font-display text-lg font-semibold text-tide-400">4G · LoRa</dd>
</div>
</dl>
</div>
<!-- Artwork: stylised rock + sonar rings -->
<div class="lg:col-span-5 relative flex justify-center">
<div class="relative w-full max-w-md aspect-square">
<!-- Sonar concentric rings -->
<div class="absolute inset-0 flex items-center justify-center">
<span class="sonar-ring absolute h-40 w-40 rounded-full border border-tide-400/50"></span>
<span class="sonar-ring absolute h-40 w-40 rounded-full border border-tide-400/50" style="animation-delay: 1.2s;"></span>
<span class="sonar-ring absolute h-40 w-40 rounded-full border border-lagoon-400/40" style="animation-delay: 2.4s;"></span>
</div>
<!-- Rock SVG -->
<svg viewBox="0 0 400 400" class="relative z-10 w-full h-full drop-shadow-[0_25px_35px_rgba(6,182,212,0.25)]" aria-hidden="true">
<defs>
<radialGradient id="rockGlow" cx="50%" cy="40%" r="60%">
<stop offset="0" stop-color="#1a4570"/>
<stop offset="0.6" stop-color="#0f2742"/>
<stop offset="1" stop-color="#050d1a"/>
</radialGradient>
<linearGradient id="waterline" x1="0" x2="1" y1="0" y2="0">
<stop offset="0" stop-color="#0ea5e9" stop-opacity="0"/>
<stop offset="0.5" stop-color="#38bdf8"/>
<stop offset="1" stop-color="#06b6d4" stop-opacity="0"/>
</linearGradient>
</defs>
<!-- underwater ambient -->
<circle cx="200" cy="220" r="180" fill="url(#rockGlow)" opacity="0.65"/>
<!-- Rock silhouette -->
<path d="M90 300
C 80 270, 95 240, 115 230
C 110 200, 135 175, 165 175
C 170 150, 200 140, 225 150
C 250 135, 285 145, 300 175
C 325 180, 335 210, 320 235
C 340 255, 335 290, 310 305
C 300 325, 270 335, 240 328
C 215 340, 180 340, 155 325
C 125 330, 95 320, 90 300 Z"
fill="#0f2742" stroke="#1a4570" stroke-width="1.5"/>
<!-- Rock highlights -->
<path d="M135 210 C 160 195, 195 195, 220 210" fill="none" stroke="#215889" stroke-width="1.4" opacity="0.7"/>
<path d="M175 260 C 210 250, 240 260, 275 250" fill="none" stroke="#143558" stroke-width="1.2" opacity="0.6"/>
<!-- Embedded sensor "eye" -->
<g transform="translate(215 225)">
<circle r="18" fill="#050d1a" stroke="#38bdf8" stroke-width="1.5"/>
<circle r="8" fill="#06b6d4"/>
<circle r="3" fill="#e0f2fe"/>
</g>
<!-- Solar panel -->
<g transform="translate(165 130) rotate(-6)">
<rect width="110" height="34" rx="3" fill="#050d1a" stroke="#215889" stroke-width="1"/>
<g stroke="#143558" stroke-width="0.8">
<line x1="0" y1="11" x2="110" y2="11"/>
<line x1="0" y1="22" x2="110" y2="22"/>
<line x1="27" y1="0" x2="27" y2="34"/>
<line x1="55" y1="0" x2="55" y2="34"/>
<line x1="82" y1="0" x2="82" y2="34"/>
</g>
<rect width="110" height="34" rx="3" fill="url(#waterline)" opacity="0.15"/>
</g>
<!-- Antenna -->
<g stroke="#38bdf8" stroke-width="1.6" stroke-linecap="round">
<line x1="250" y1="135" x2="250" y2="95"/>
<circle cx="250" cy="90" r="3" fill="#38bdf8"/>
</g>
<!-- Waterline -->
<path d="M20 160 Q 100 145 200 160 T 380 160" stroke="url(#waterline)" stroke-width="1.3" fill="none" opacity="0.9"/>
<path d="M20 170 Q 110 158 210 172 T 380 170" stroke="#0ea5e9" stroke-width="0.8" fill="none" opacity="0.4"/>
<!-- Sound ripples coming from rock -->
<g stroke="#38bdf8" fill="none" opacity="0.55">
<circle cx="215" cy="225" r="46" stroke-dasharray="2 3"/>
<circle cx="215" cy="225" r="70" stroke-dasharray="2 5" opacity="0.35"/>
</g>
<!-- Seabed hints -->
<g fill="#0a1a2f" opacity="0.9">
<ellipse cx="120" cy="348" rx="30" ry="6"/>
<ellipse cx="220" cy="360" rx="70" ry="7"/>
<ellipse cx="320" cy="352" rx="35" ry="5"/>
</g>
</svg>
</div>
</div>
</div>
</div>
<!-- subtle bottom fade line -->
<div class="absolute bottom-0 left-0 right-0 h-px divider"></div>
</section>

45
src/components/Nav.astro Normal file
View File

@@ -0,0 +1,45 @@
---
const base = import.meta.env.BASE_URL;
const items = [
{ href: '#concept', label: 'Concept' },
{ href: '#pourquoi', label: 'Pourquoi' },
{ href: '#configurations', label: 'Configurations' },
{ href: '#architecture', label: 'Architecture' },
{ href: '#cas-dusage', label: "Cas d'usage" },
{ href: '#equipe', label: 'Équipe' },
{ href: '#roadmap', label: 'Feuille de route' },
{ href: '#contact', label: 'Contact' },
];
---
<header class="sticky top-0 z-40 border-b border-white/5 bg-abyss-950/75 backdrop-blur">
<div class="container-narrow flex h-16 items-center justify-between">
<a href={base} class="flex items-center gap-2.5" aria-label="NowYouSea — retour à l'accueil">
<span class="relative inline-flex h-8 w-8 items-center justify-center rounded-md bg-gradient-to-br from-tide-500 to-lagoon-500 shadow-glow">
<svg viewBox="0 0 24 24" class="h-5 w-5 text-abyss-950" fill="currentColor" aria-hidden="true">
<path d="M2 16c2 0 2-2 4-2s2 2 4 2 2-2 4-2 2 2 4 2 2-2 4-2v4H2z"/>
<circle cx="12" cy="9" r="2"/>
<path d="M7 9a5 5 0 0 1 10 0" stroke="currentColor" stroke-width="1.5" fill="none" opacity=".65"/>
</svg>
</span>
<span class="font-display text-base font-semibold tracking-tight">NowYouSea</span>
</a>
<nav aria-label="Navigation principale" class="hidden lg:block">
<ul class="flex items-center gap-7 text-sm text-foam-100/75">
{items.map((item) => (
<li>
<a href={item.href} class="hover:text-tide-400">{item.label}</a>
</li>
))}
</ul>
</nav>
<a href="#contact" class="btn btn-primary hidden md:inline-flex">
Demander une démo
<svg viewBox="0 0 24 24" class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M5 12h14M13 5l7 7-7 7"/>
</svg>
</a>
</div>
</header>

60
src/layouts/Layout.astro Normal file
View File

@@ -0,0 +1,60 @@
---
import '../styles/global.css';
interface Props {
title?: string;
description?: string;
}
const {
title = "NowYouSea — Le rocher connecté qui écoute l'océan",
description = "Réseau de stations côtières acoustiques low-cost pour la surveillance du milieu marin. Hydrophone, physico-chimie et calcul embarqué, du terrain au dashboard.",
} = Astro.props;
const base = import.meta.env.BASE_URL;
---
<!doctype html>
<html lang="fr" class="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content={description} />
<meta name="theme-color" content="#050d1a" />
<meta name="color-scheme" content="dark" />
<link rel="icon" type="image/svg+xml" href={`${base}favicon.svg`} />
<link rel="preconnect" href="https://rsms.me/" />
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Sora:wght@400;500;600;700&display=swap" />
<meta property="og:type" content="website" />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta name="twitter:card" content="summary_large_image" />
<title>{title}</title>
</head>
<body>
<a
href="#main"
class="sr-only focus:not-sr-only focus:fixed focus:top-4 focus:left-4 focus:z-50 focus:rounded focus:bg-tide-500 focus:px-4 focus:py-2 focus:text-abyss-950"
>
Aller au contenu
</a>
<slot />
</body>
</html>
<style is:global>
html {
font-family: 'Inter', ui-sans-serif, system-ui, sans-serif;
font-feature-settings: 'cv11', 'ss01', 'ss03';
}
@supports (font-variation-settings: normal) {
html {
font-family: 'Inter var', ui-sans-serif, system-ui, sans-serif;
}
}
</style>

135
src/styles/global.css Normal file
View File

@@ -0,0 +1,135 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
color-scheme: dark;
}
html {
scroll-behavior: smooth;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
@apply bg-abyss-950 text-foam-50 font-sans;
background-image:
radial-gradient(1200px 600px at 10% -10%, rgba(6, 182, 212, 0.10), transparent 60%),
radial-gradient(1000px 500px at 110% 10%, rgba(14, 165, 233, 0.08), transparent 60%),
linear-gradient(180deg, #050d1a 0%, #0a1a2f 100%);
background-attachment: fixed;
min-height: 100vh;
}
h1, h2, h3, h4 {
@apply font-display tracking-tight;
}
::selection {
@apply bg-lagoon-500/40 text-foam-50;
}
a {
@apply transition-colors;
}
/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
scroll-behavior: auto !important;
}
}
}
@layer components {
.container-narrow {
@apply mx-auto w-full max-w-6xl px-6 md:px-10;
}
.eyebrow {
@apply inline-flex items-center gap-2 text-xs font-medium uppercase tracking-[0.2em] text-lagoon-400;
}
.eyebrow::before {
content: "";
@apply inline-block h-px w-8 bg-lagoon-400/60;
}
.h-display {
@apply font-display font-semibold tracking-tight text-4xl md:text-5xl lg:text-6xl leading-[1.05];
}
.h-section {
@apply font-display font-semibold tracking-tight text-3xl md:text-4xl lg:text-5xl leading-[1.1];
}
.lead {
@apply text-lg md:text-xl text-foam-100/80 leading-relaxed;
}
.glass {
@apply bg-white/[0.03] backdrop-blur-sm border border-white/[0.07];
}
.card {
@apply glass rounded-2xl p-6 md:p-8 shadow-card;
}
.btn {
@apply inline-flex items-center gap-2 rounded-full px-5 py-3 text-sm font-medium transition;
@apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-tide-400 focus-visible:ring-offset-2 focus-visible:ring-offset-abyss-950;
}
.btn-primary {
@apply bg-tide-500 text-abyss-950 hover:bg-tide-400;
}
.btn-ghost {
@apply border border-white/15 text-foam-50 hover:border-tide-400/60 hover:text-tide-400;
}
.divider {
height: 1px;
background: linear-gradient(90deg, transparent, rgba(56, 189, 248, 0.35), transparent);
}
.section {
@apply relative py-20 md:py-28;
}
.section-title-block {
@apply mb-12 md:mb-16 max-w-3xl;
}
}
/* Decorative caustic glow used as subtle ambient light */
.caustic-bg {
background-image:
radial-gradient(circle at 20% 10%, rgba(6, 182, 212, 0.12), transparent 55%),
radial-gradient(circle at 80% 80%, rgba(14, 165, 233, 0.10), transparent 55%);
}
/* Animated horizon line */
@keyframes drift {
0%, 100% { transform: translateX(0) translateY(0); }
50% { transform: translateX(12px) translateY(-6px); }
}
.drift-slow {
animation: drift 14s ease-in-out infinite;
}
/* Sonar ring */
@keyframes sonar-ring {
0% { transform: scale(0.8); opacity: 0.65; }
100% { transform: scale(2.8); opacity: 0; }
}
.sonar-ring {
animation: sonar-ring 3.6s ease-out infinite;
}

72
tailwind.config.mjs Normal file
View File

@@ -0,0 +1,72 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
darkMode: 'class',
theme: {
extend: {
colors: {
abyss: {
950: '#050d1a',
900: '#0a1a2f',
800: '#0f2742',
700: '#143558',
600: '#1a4570',
500: '#215889',
},
tide: {
400: '#38bdf8',
500: '#0ea5e9',
600: '#0284c7',
},
foam: {
50: '#f0f9ff',
100: '#e0f2fe',
200: '#bae6fd',
},
lagoon: {
400: '#22d3ee',
500: '#06b6d4',
600: '#0891b2',
},
galet: {
300: '#94a3b8',
400: '#64748b',
500: '#475569',
},
},
fontFamily: {
sans: ['Inter', 'ui-sans-serif', 'system-ui', 'sans-serif'],
display: ['Sora', 'Inter', 'ui-sans-serif', 'system-ui', 'sans-serif'],
},
boxShadow: {
glow: '0 0 40px -10px rgba(14, 165, 233, 0.35)',
'glow-lg': '0 0 80px -10px rgba(6, 182, 212, 0.45)',
card: '0 8px 30px rgba(5, 13, 26, 0.45)',
},
backgroundImage: {
'abyss-grad': 'linear-gradient(180deg, #050d1a 0%, #0a1a2f 40%, #0f2742 100%)',
'caustic': 'radial-gradient(circle at 20% 10%, rgba(6,182,212,0.15), transparent 50%), radial-gradient(circle at 80% 80%, rgba(14,165,233,0.12), transparent 50%)',
},
keyframes: {
'float-slow': {
'0%, 100%': { transform: 'translateY(0px)' },
'50%': { transform: 'translateY(-12px)' },
},
'shimmer': {
'0%': { backgroundPosition: '-200% 0' },
'100%': { backgroundPosition: '200% 0' },
},
'sonar': {
'0%': { transform: 'scale(1)', opacity: '0.6' },
'100%': { transform: 'scale(3)', opacity: '0' },
},
},
animation: {
'float-slow': 'float-slow 8s ease-in-out infinite',
'shimmer': 'shimmer 6s linear infinite',
'sonar': 'sonar 3s ease-out infinite',
},
},
},
plugins: [],
};

9
tsconfig.json Normal file
View File

@@ -0,0 +1,9 @@
{
"extends": "astro/tsconfigs/strict",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"~/*": ["src/*"]
}
}
}