diff options
Diffstat (limited to 'landing')
| -rw-r--r-- | landing/.gitignore | 4 | ||||
| -rw-r--r-- | landing/content/_index.md | 4 | ||||
| -rw-r--r-- | landing/hugo.toml | 19 | ||||
| -rw-r--r-- | landing/layouts/_default/baseof.html | 32 | ||||
| -rw-r--r-- | landing/layouts/index.html | 223 | ||||
| -rw-r--r-- | landing/layouts/partials/footer.html | 16 | ||||
| -rw-r--r-- | landing/layouts/partials/header.html | 20 | ||||
| -rw-r--r-- | landing/static/css/style.css | 637 | ||||
| -rw-r--r-- | landing/static/favicon.svg | 6 | ||||
| -rw-r--r-- | landing/static/robots.txt | 4 |
10 files changed, 965 insertions, 0 deletions
diff --git a/landing/.gitignore b/landing/.gitignore new file mode 100644 index 0000000..9623b27 --- /dev/null +++ b/landing/.gitignore @@ -0,0 +1,4 @@ +# Hugo build output +public/ +resources/ +.hugo_build.lock diff --git a/landing/content/_index.md b/landing/content/_index.md new file mode 100644 index 0000000..53f3714 --- /dev/null +++ b/landing/content/_index.md @@ -0,0 +1,4 @@ +--- +title: "Tidy Index" +description: "Clean, well-structured data delivered through APIs built for humans, machines, and LLM agents." +--- diff --git a/landing/hugo.toml b/landing/hugo.toml new file mode 100644 index 0000000..82cb835 --- /dev/null +++ b/landing/hugo.toml @@ -0,0 +1,19 @@ +baseURL = "https://tidyindex.com/" +languageCode = "en-us" +title = "Tidy Index — Structured data, served simply" +enableRobotsTXT = true +disableKinds = ["taxonomy", "term"] + +[params] + description = "Clean, well-structured data delivered through APIs built for humans, machines, and LLM agents." + tagline = "Structured data, served simply." + +[markup] + [markup.goldmark] + [markup.goldmark.renderer] + unsafe = true + +[minify] + disableHTML = false + disableCSS = false + disableJS = false diff --git a/landing/layouts/_default/baseof.html b/landing/layouts/_default/baseof.html new file mode 100644 index 0000000..89a0bd7 --- /dev/null +++ b/landing/layouts/_default/baseof.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html lang="{{ .Site.LanguageCode | default "en" }}"> +<head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>{{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} — {{ .Site.Title }}{{ end }}</title> + <meta name="description" content="{{ with .Description }}{{ . }}{{ else }}{{ .Site.Params.description }}{{ end }}" /> + + <meta property="og:title" content="{{ .Site.Title }}" /> + <meta property="og:description" content="{{ .Site.Params.description }}" /> + <meta property="og:type" content="website" /> + <meta property="og:url" content="{{ .Site.BaseURL }}" /> + + <meta name="twitter:card" content="summary_large_image" /> + <meta name="twitter:title" content="{{ .Site.Title }}" /> + <meta name="twitter:description" content="{{ .Site.Params.description }}" /> + + <link rel="preconnect" href="https://fonts.googleapis.com"> + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> + <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet"> + + <link rel="stylesheet" href="{{ "css/style.css" | relURL }}" /> + <link rel="icon" type="image/svg+xml" href="{{ "favicon.svg" | relURL }}" /> +</head> +<body> + {{ partial "header.html" . }} + <main> + {{ block "main" . }}{{ end }} + </main> + {{ partial "footer.html" . }} +</body> +</html> diff --git a/landing/layouts/index.html b/landing/layouts/index.html new file mode 100644 index 0000000..231ff61 --- /dev/null +++ b/landing/layouts/index.html @@ -0,0 +1,223 @@ +{{ define "main" }} + +<section class="hero"> + <div class="container hero-inner"> + <span class="eyebrow">Clean data, on tap</span> + <h1 class="hero-title"> + Structured data,<br /> + <span class="accent">served simply.</span> + </h1> + <p class="hero-sub"> + Tidy Index delivers clean, well-organized datasets through a single API + designed for humans, machines, and LLM agents alike. + </p> + <div class="hero-cta"> + <a href="#contact" class="btn btn-primary">Request access</a> + <a href="#how" class="btn btn-ghost">See how it works</a> + </div> + + <div class="hero-visual" aria-hidden="true"> + <div class="code-card"> + <div class="code-card-head"> + <span class="dot dot-a"></span> + <span class="dot dot-b"></span> + <span class="dot dot-c"></span> + <span class="code-card-title">GET /v1/datasets/cities</span> + </div> +<pre class="code-card-body"><code><span class="tk-p">{</span> + <span class="tk-k">"id"</span>: <span class="tk-s">"cities"</span>, + <span class="tk-k">"updated"</span>: <span class="tk-s">"2026-04-09T08:14:00Z"</span>, + <span class="tk-k">"records"</span>: <span class="tk-p">[</span> + <span class="tk-p">{</span> <span class="tk-k">"name"</span>: <span class="tk-s">"Lisbon"</span>, <span class="tk-k">"population"</span>: <span class="tk-n">548703</span> <span class="tk-p">}</span>, + <span class="tk-p">{</span> <span class="tk-k">"name"</span>: <span class="tk-s">"Kyoto"</span>, <span class="tk-k">"population"</span>: <span class="tk-n">1463723</span> <span class="tk-p">}</span>, + <span class="tk-p">{</span> <span class="tk-k">"name"</span>: <span class="tk-s">"Quito"</span>, <span class="tk-k">"population"</span>: <span class="tk-n">2011388</span> <span class="tk-p">}</span> + <span class="tk-p">]</span> +<span class="tk-p">}</span></code></pre> + </div> + </div> + </div> +</section> + +<section id="what" class="section"> + <div class="container narrow"> + <span class="eyebrow center">What is Tidy Index</span> + <h2 class="section-title center">We’ve already done the cleanup.</h2> + <p class="section-lede center"> + Scraping. Reformatting. Reconciling. Re-scraping when the source changes + shape. We do all of that — once, properly — so you don’t + have to do any of it. + </p> + </div> +</section> + +<section id="formats" class="section section-soft"> + <div class="container"> + <span class="eyebrow center">Pick your shape</span> + <h2 class="section-title center">One dataset. Every shape you need.</h2> + <p class="section-lede center"> + Ask for JSON, get JSON. Ask for chunks, get chunks. Same data underneath — + no reformatting, no glue code. + </p> + + <div class="format-grid"> + <article class="format-card"> + <div class="format-icon" aria-hidden="true"> + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"> + <path d="M8 4 4 12l4 8"/> + <path d="m16 4 4 8-4 8"/> + </svg> + </div> + <h3>JSON</h3> + <p>Stable schemas, stable IDs, ETags on every response. Drop it straight into your code.</p> + </article> + + <article class="format-card"> + <div class="format-icon" aria-hidden="true"> + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"> + <path d="M4 4h16v16H4z"/> + <path d="M4 9h16"/> + <path d="M9 4v16"/> + </svg> + </div> + <h3>XML & CSV</h3> + <p>For the pipelines that have been around longer than your team has. Strict, well-formed, no surprises.</p> + </article> + + <article class="format-card"> + <div class="format-icon" aria-hidden="true"> + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"> + <circle cx="12" cy="12" r="8"/> + <path d="M12 8v4l2 2"/> + </svg> + </div> + <h3>LLM chunks</h3> + <p>Already chunked, already cited, already embedding-ready. Drop them into your retriever and stop fighting tokenizers.</p> + </article> + + <article class="format-card"> + <div class="format-icon" aria-hidden="true"> + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"> + <path d="M3 12h4l3-8 4 16 3-8h4"/> + </svg> + </div> + <h3>Streaming</h3> + <p>Webhooks and server-sent events the moment something changes. No polling, no stale caches.</p> + </article> + </div> + </div> +</section> + +<section id="how" class="section"> + <div class="container"> + <span class="eyebrow center">Who it’s for</span> + <h2 class="section-title center">Same data. Three kinds of readers.</h2> + <p class="section-lede center"> + People, programs, and agents all want different things from the same + dataset. Tidy Index gives each of them what they expect. + </p> + + <div class="audience-grid"> + <div class="audience-card"> + <div class="audience-num">01</div> + <h3>For humans</h3> + <p> + A real web UI. Skim a schema, preview a few rows, grab a snapshot, + and copy a working curl command without leaving the page. + </p> + </div> + + <div class="audience-card audience-card-feature"> + <div class="audience-num">02</div> + <h3>For computers</h3> + <p> + Boring REST, the way you like it. OpenAPI specs, semver, ETags, + idempotent reads, and rate limits we’ll actually tell you about. + </p> + </div> + + <div class="audience-card"> + <div class="audience-num">03</div> + <h3>For LLM agents</h3> + <p> + Endpoints shaped like tool calls. Chunks shaped like context. Every + response carries its own provenance, so the model never has to guess + where the data came from. + </p> + </div> + </div> + </div> +</section> + +<section class="section section-soft"> + <div class="container narrow"> + <span class="eyebrow center">Why Tidy Index</span> + <h2 class="section-title center">Less wrangling. More building.</h2> + + <ul class="value-list"> + <li> + <span class="value-check" aria-hidden="true"> + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> + <polyline points="20 6 9 17 4 12"/> + </svg> + </span> + <div> + <strong>Curated, not crawled.</strong> + <p>A human looks at every dataset before it ships. We’d rather have ten that are right than ten thousand that are almost right.</p> + </div> + </li> + <li> + <span class="value-check" aria-hidden="true"> + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> + <polyline points="20 6 9 17 4 12"/> + </svg> + </span> + <div> + <strong>Schemas that don’t move under you.</strong> + <p>Versioned endpoints, deprecation windows, and a changelog you can subscribe to. If something’s about to break, you’ll be the first to know.</p> + </div> + </li> + <li> + <span class="value-check" aria-hidden="true"> + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> + <polyline points="20 6 9 17 4 12"/> + </svg> + </span> + <div> + <strong>Every record has a receipt.</strong> + <p>Each row links back to where it came from. Your auditors will love it. Your models will stop making things up.</p> + </div> + </li> + <li> + <span class="value-check" aria-hidden="true"> + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> + <polyline points="20 6 9 17 4 12"/> + </svg> + </span> + <div> + <strong>One key, every format.</strong> + <p>JSON, XML, CSV, or LLM chunks. Flip a header — same data, different shape.</p> + </div> + </li> + </ul> + </div> +</section> + +<section id="contact" class="section"> + <div class="container narrow"> + <div class="cta-card"> + <span class="cta-glow" aria-hidden="true"></span> + <h2 class="cta-title">Ready when you are.</h2> + <a href="mailto:hello@tidyindex.com" class="btn btn-primary btn-lg cta-btn"> + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> + <circle cx="7.5" cy="15.5" r="5.5"/> + <path d="m11.5 11.5 9.5-9.5"/> + <path d="m17 5 3 3"/> + <path d="m14 8 3 3"/> + </svg> + API Key + </a> + </div> + </div> +</section> + +{{ end }} diff --git a/landing/layouts/partials/footer.html b/landing/layouts/partials/footer.html new file mode 100644 index 0000000..d6588fe --- /dev/null +++ b/landing/layouts/partials/footer.html @@ -0,0 +1,16 @@ +<footer class="site-footer"> + <div class="container footer-inner"> + <div class="footer-brand"> + <span class="brand-mark" aria-hidden="true"> + <svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"> + <rect x="4" y="6" width="24" height="3" rx="1.5" fill="currentColor"/> + <rect x="4" y="14" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.7"/> + <rect x="4" y="22" width="12" height="3" rx="1.5" fill="currentColor" opacity="0.4"/> + </svg> + </span> + <span>Tidy Index</span> + </div> + <p class="footer-tagline">Structured data, served simply.</p> + <p class="footer-meta">© {{ now.Year }} Tidy Index. All rights reserved.</p> + </div> +</footer> diff --git a/landing/layouts/partials/header.html b/landing/layouts/partials/header.html new file mode 100644 index 0000000..5f6d85b --- /dev/null +++ b/landing/layouts/partials/header.html @@ -0,0 +1,20 @@ +<header class="site-header"> + <div class="container header-inner"> + <a href="{{ "/" | relURL }}" class="brand" aria-label="Tidy Index home"> + <span class="brand-mark" aria-hidden="true"> + <svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"> + <rect x="4" y="6" width="24" height="3" rx="1.5" fill="currentColor"/> + <rect x="4" y="14" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.7"/> + <rect x="4" y="22" width="12" height="3" rx="1.5" fill="currentColor" opacity="0.4"/> + </svg> + </span> + <span class="brand-name">Tidy Index</span> + </a> + <nav class="site-nav" aria-label="Primary"> + <a href="#what">What</a> + <a href="#formats">Formats</a> + <a href="#how">How it works</a> + <a href="#contact" class="nav-cta">Get access</a> + </nav> + </div> +</header> diff --git a/landing/static/css/style.css b/landing/static/css/style.css new file mode 100644 index 0000000..30665f6 --- /dev/null +++ b/landing/static/css/style.css @@ -0,0 +1,637 @@ +/* ---------------------------------------------------------------- + Tidy Index — landing styles + Palette: whites, soft blues, generous space. +---------------------------------------------------------------- */ + +:root { + --c-bg: #ffffff; + --c-bg-soft: #f6f9fc; + --c-bg-tint: #eef4fb; + --c-border: #e3ecf5; + --c-border-soft: #eef2f7; + + --c-ink: #0b1f3a; + --c-ink-soft: #3b4f6b; + --c-ink-mute: #6b7c93; + + --c-blue: #3b82f6; + --c-blue-deep: #2563eb; + --c-blue-soft: #dbeafe; + --c-blue-tint: #eef4ff; + + --c-accent: #60a5fa; + + --shadow-sm: 0 1px 2px rgba(15, 38, 73, 0.04); + --shadow-md: 0 6px 24px -8px rgba(37, 99, 235, 0.18), + 0 2px 6px rgba(15, 38, 73, 0.04); + --shadow-lg: 0 24px 60px -20px rgba(37, 99, 235, 0.25), + 0 6px 16px rgba(15, 38, 73, 0.06); + + --radius-sm: 8px; + --radius-md: 14px; + --radius-lg: 22px; + + --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', + Helvetica, Arial, sans-serif; + --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, + Consolas, monospace; + + --max-w: 1120px; + --max-w-narrow: 760px; +} + +* { box-sizing: border-box; } + +html { -webkit-text-size-adjust: 100%; } + +body { + margin: 0; + font-family: var(--font-sans); + font-size: 17px; + line-height: 1.6; + color: var(--c-ink); + background: var(--c-bg); + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + background-image: + radial-gradient(ellipse 80% 50% at 50% -10%, var(--c-blue-tint), transparent 70%), + radial-gradient(ellipse 60% 40% at 90% 10%, #f0f7ff, transparent 70%); + background-repeat: no-repeat; +} + +a { + color: var(--c-blue-deep); + text-decoration: none; + transition: color 160ms ease; +} +a:hover { color: var(--c-blue); } + +img, svg { display: block; max-width: 100%; } + +/* ---------- layout helpers ---------- */ + +.container { + width: 100%; + max-width: var(--max-w); + margin: 0 auto; + padding: 0 24px; +} + +.container.narrow { max-width: var(--max-w-narrow); } + +.eyebrow { + display: inline-block; + font-size: 13px; + font-weight: 500; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--c-blue-deep); + background: var(--c-blue-tint); + padding: 6px 12px; + border-radius: 999px; + border: 1px solid var(--c-blue-soft); +} +.eyebrow.center { display: inline-block; } + +.center { text-align: center; } +.center.eyebrow { display: inline-block; } + +.section { + padding: 120px 0; +} + +.section-soft { + background: var(--c-bg-soft); + border-top: 1px solid var(--c-border-soft); + border-bottom: 1px solid var(--c-border-soft); +} + +.section-title { + font-size: clamp(28px, 4vw, 44px); + line-height: 1.15; + font-weight: 600; + letter-spacing: -0.02em; + margin: 18px 0 18px; + color: var(--c-ink); +} +.section-title.center { text-align: center; max-width: 720px; margin-left: auto; margin-right: auto; } + +.section-lede { + font-size: 19px; + color: var(--c-ink-soft); + max-width: 640px; + margin: 0 auto 60px; + line-height: 1.6; +} +.section-lede.center { text-align: center; } + +/* center the eyebrow when wrapped */ +.section .eyebrow.center, +.container .eyebrow.center { + display: block; + margin-left: auto; + margin-right: auto; + width: max-content; +} + +/* ---------- header ---------- */ + +.site-header { + position: sticky; + top: 0; + z-index: 50; + background: rgba(255, 255, 255, 0.78); + backdrop-filter: saturate(180%) blur(14px); + -webkit-backdrop-filter: saturate(180%) blur(14px); + border-bottom: 1px solid var(--c-border-soft); +} + +.header-inner { + display: flex; + align-items: center; + justify-content: space-between; + height: 72px; +} + +.brand { + display: inline-flex; + align-items: center; + gap: 10px; + font-weight: 600; + font-size: 17px; + color: var(--c-ink); + letter-spacing: -0.01em; +} +.brand:hover { color: var(--c-blue-deep); } + +.brand-mark { + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; height: 32px; + color: var(--c-blue-deep); + background: var(--c-blue-tint); + border-radius: 9px; + border: 1px solid var(--c-blue-soft); +} +.brand-mark svg { width: 20px; height: 20px; } + +.site-nav { + display: flex; + align-items: center; + gap: 32px; +} +.site-nav a { + color: var(--c-ink-soft); + font-size: 15px; + font-weight: 500; +} +.site-nav a:hover { color: var(--c-blue-deep); } + +.nav-cta { + background: var(--c-ink); + color: #fff !important; + padding: 9px 16px; + border-radius: 999px; + transition: transform 160ms ease, background 160ms ease; +} +.nav-cta:hover { + background: var(--c-blue-deep); + transform: translateY(-1px); +} + +/* ---------- buttons ---------- */ + +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + font-family: inherit; + font-size: 16px; + font-weight: 500; + padding: 14px 24px; + border-radius: 999px; + border: 1px solid transparent; + cursor: pointer; + transition: all 180ms ease; + letter-spacing: -0.005em; +} + +.btn-primary { + background: var(--c-blue-deep); + color: #fff; + box-shadow: 0 6px 18px -6px rgba(37, 99, 235, 0.5); +} +.btn-primary:hover { + background: #1d4ed8; + color: #fff; + transform: translateY(-1px); + box-shadow: 0 10px 24px -8px rgba(37, 99, 235, 0.55); +} + +.btn-ghost { + background: transparent; + color: var(--c-ink); + border-color: var(--c-border); +} +.btn-ghost:hover { + background: #fff; + border-color: var(--c-blue-soft); + color: var(--c-blue-deep); +} + +.btn-lg { + padding: 16px 30px; + font-size: 16px; +} + +/* ---------- hero ---------- */ + +.hero { + padding: 120px 0 80px; + position: relative; + overflow: hidden; +} + +.hero-inner { + text-align: center; + position: relative; +} + +.hero .eyebrow { + margin-bottom: 28px; +} + +.hero-title { + font-size: clamp(40px, 6vw, 76px); + line-height: 1.05; + font-weight: 600; + letter-spacing: -0.035em; + margin: 0 auto 24px; + max-width: 12ch; + color: var(--c-ink); +} + +.hero-title .accent { + background: linear-gradient(120deg, var(--c-blue-deep), var(--c-accent)); + -webkit-background-clip: text; + background-clip: text; + color: transparent; +} + +.hero-sub { + font-size: 20px; + line-height: 1.55; + color: var(--c-ink-soft); + max-width: 600px; + margin: 0 auto 40px; +} + +.hero-cta { + display: flex; + gap: 14px; + justify-content: center; + flex-wrap: wrap; + margin-bottom: 80px; +} + +/* ---------- code card ---------- */ + +.hero-visual { + display: flex; + justify-content: center; + margin-top: 16px; +} + +.code-card { + width: 100%; + max-width: 640px; + background: #ffffff; + border: 1px solid var(--c-border); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-lg); + overflow: hidden; + text-align: left; + position: relative; +} + +.code-card::before { + content: ""; + position: absolute; + inset: -1px; + border-radius: inherit; + padding: 1px; + background: linear-gradient(160deg, rgba(96,165,250,0.4), rgba(255,255,255,0) 60%); + -webkit-mask: + linear-gradient(#000 0 0) content-box, + linear-gradient(#000 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; +} + +.code-card-head { + display: flex; + align-items: center; + gap: 8px; + padding: 14px 18px; + background: var(--c-bg-soft); + border-bottom: 1px solid var(--c-border-soft); +} + +.code-card-head .dot { + width: 10px; height: 10px; + border-radius: 50%; + background: #d6dde7; +} +.dot-a { background: #ffd1d1; } +.dot-b { background: #ffe5b4; } +.dot-c { background: #c8e6c9; } + +.code-card-title { + margin-left: 12px; + font-family: var(--font-mono); + font-size: 13px; + color: var(--c-ink-mute); +} + +.code-card-body { + margin: 0; + padding: 22px 24px; + font-family: var(--font-mono); + font-size: 13.5px; + line-height: 1.65; + color: var(--c-ink-soft); + background: #ffffff; + overflow-x: auto; + white-space: pre; +} +.code-card-body code { font-family: inherit; } + +.tk-k { color: #2563eb; } +.tk-s { color: #0f766e; } +.tk-n { color: #b45309; } +.tk-p { color: #94a3b8; } + +/* ---------- format grid ---------- */ + +.format-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 20px; + margin-top: 20px; +} + +.format-card { + background: #fff; + border: 1px solid var(--c-border); + border-radius: var(--radius-md); + padding: 32px 26px; + box-shadow: var(--shadow-sm); + transition: transform 200ms ease, box-shadow 200ms ease, border-color 200ms ease; +} + +.format-card:hover { + transform: translateY(-3px); + border-color: var(--c-blue-soft); + box-shadow: var(--shadow-md); +} + +.format-icon { + display: inline-flex; + width: 44px; + height: 44px; + align-items: center; + justify-content: center; + border-radius: 12px; + background: var(--c-blue-tint); + color: var(--c-blue-deep); + margin-bottom: 18px; +} +.format-icon svg { width: 22px; height: 22px; } + +.format-card h3 { + margin: 0 0 8px; + font-size: 18px; + font-weight: 600; + letter-spacing: -0.01em; + color: var(--c-ink); +} + +.format-card p { + margin: 0; + font-size: 15px; + line-height: 1.55; + color: var(--c-ink-mute); +} + +/* ---------- audience grid ---------- */ + +.audience-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 24px; + margin-top: 20px; +} + +.audience-card { + position: relative; + background: #fff; + border: 1px solid var(--c-border); + border-radius: var(--radius-lg); + padding: 40px 32px; + box-shadow: var(--shadow-sm); + transition: transform 200ms ease, box-shadow 200ms ease; +} + +.audience-card:hover { + transform: translateY(-3px); + box-shadow: var(--shadow-md); +} + +.audience-card-feature { + background: linear-gradient(180deg, #ffffff 0%, var(--c-blue-tint) 130%); + border-color: var(--c-blue-soft); +} + +.audience-num { + font-family: var(--font-mono); + font-size: 13px; + font-weight: 500; + color: var(--c-blue-deep); + letter-spacing: 0.04em; + margin-bottom: 16px; +} + +.audience-card h3 { + margin: 0 0 12px; + font-size: 22px; + font-weight: 600; + letter-spacing: -0.015em; + color: var(--c-ink); +} + +.audience-card p { + margin: 0; + color: var(--c-ink-soft); + font-size: 16px; + line-height: 1.6; +} + +/* ---------- value list ---------- */ + +.value-list { + list-style: none; + padding: 0; + margin: 40px 0 0; + display: grid; + gap: 22px; +} + +.value-list li { + display: flex; + align-items: flex-start; + gap: 18px; + background: #fff; + border: 1px solid var(--c-border); + border-radius: var(--radius-md); + padding: 22px 26px; + box-shadow: var(--shadow-sm); +} + +.value-check { + display: inline-flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + flex: 0 0 36px; + border-radius: 50%; + background: var(--c-blue-tint); + color: var(--c-blue-deep); +} +.value-check svg { width: 18px; height: 18px; } + +.value-list strong { + display: block; + font-weight: 600; + font-size: 17px; + color: var(--c-ink); + margin-bottom: 4px; +} + +.value-list p { + margin: 0; + color: var(--c-ink-mute); + font-size: 15.5px; + line-height: 1.55; +} + +/* ---------- CTA card ---------- */ + +.cta-card { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + background: linear-gradient(160deg, #ffffff 0%, var(--c-blue-tint) 100%); + border: 1px solid var(--c-blue-soft); + border-radius: var(--radius-lg); + padding: 72px 40px; + box-shadow: var(--shadow-md); + overflow: hidden; +} + +.cta-glow { + position: absolute; + top: -120px; + left: 50%; + width: 360px; + height: 360px; + transform: translateX(-50%); + background: radial-gradient(closest-side, rgba(96, 165, 250, 0.35), rgba(96, 165, 250, 0) 70%); + pointer-events: none; + z-index: 0; +} + +.cta-title { + position: relative; + z-index: 1; + margin: 0 0 28px; + font-size: clamp(26px, 3.4vw, 36px); + font-weight: 600; + letter-spacing: -0.02em; + color: var(--c-ink); + text-align: center; +} + +.cta-btn { + position: relative; + z-index: 1; + gap: 10px; +} + +.cta-btn svg { + width: 18px; + height: 18px; +} + +/* ---------- footer ---------- */ + +.site-footer { + border-top: 1px solid var(--c-border-soft); + background: var(--c-bg); + padding: 60px 0 50px; +} + +.footer-inner { + text-align: center; +} + +.footer-brand { + display: inline-flex; + align-items: center; + gap: 10px; + font-weight: 600; + color: var(--c-ink); + margin-bottom: 14px; +} +.footer-brand .brand-mark { color: var(--c-blue-deep); } +.footer-brand .brand-mark svg { width: 20px; height: 20px; } + +.footer-tagline { + margin: 0 0 12px; + color: var(--c-ink-soft); + font-size: 15px; +} + +.footer-meta { + margin: 0; + color: var(--c-ink-mute); + font-size: 13px; +} + +/* ---------- responsive ---------- */ + +@media (max-width: 880px) { + .format-grid { grid-template-columns: repeat(2, 1fr); } + .audience-grid { grid-template-columns: 1fr; } + .section { padding: 90px 0; } + .hero { padding: 80px 0 40px; } +} + +@media (max-width: 640px) { + body { font-size: 16px; } + .site-nav { gap: 18px; } + .site-nav a:not(.nav-cta) { display: none; } + .header-inner { height: 64px; } + .hero-cta { flex-direction: column; align-items: stretch; } + .hero-cta .btn { width: 100%; } + .code-card-body { font-size: 12px; padding: 18px; } + .format-grid { grid-template-columns: 1fr; } + .cta-card { padding: 40px 24px; } +} + +@media (prefers-reduced-motion: reduce) { + * { transition: none !important; } +} diff --git a/landing/static/favicon.svg b/landing/static/favicon.svg new file mode 100644 index 0000000..256ac13 --- /dev/null +++ b/landing/static/favicon.svg @@ -0,0 +1,6 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"> + <rect width="32" height="32" rx="7" fill="#eef4ff"/> + <rect x="6" y="8" width="20" height="3" rx="1.5" fill="#2563eb"/> + <rect x="6" y="15" width="15" height="3" rx="1.5" fill="#2563eb" opacity="0.7"/> + <rect x="6" y="22" width="10" height="3" rx="1.5" fill="#2563eb" opacity="0.4"/> +</svg> diff --git a/landing/static/robots.txt b/landing/static/robots.txt new file mode 100644 index 0000000..f0d8319 --- /dev/null +++ b/landing/static/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Allow: / + +Sitemap: https://tidyindex.com/sitemap.xml |
