first commit

This commit is contained in:
Blake Ridgway
2026-07-03 21:16:24 -05:00
commit 3d4e2d4b1a
14 changed files with 1447 additions and 0 deletions

18
.dockerignore Normal file
View File

@@ -0,0 +1,18 @@
# Compiled binaries (built inside Docker)
arcline-landing
landing
# Git
.git
.gitignore
# Docker
Dockerfile
docker-compose.yml
# CI/CD
.gitlab-ci.yml
# Editor
.vscode
.idea

49
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,49 @@
stages:
- build
- deploy
variables:
IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
IMAGE_LATEST: $CI_REGISTRY_IMAGE:latest
build:
stage: build
image: docker:26
services:
- docker:26-dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker pull $IMAGE_LATEST || true
- docker build --pull --cache-from $IMAGE_LATEST --tag $IMAGE --tag $IMAGE_LATEST .
- docker push $IMAGE
- docker push $IMAGE_LATEST
only:
- main
deploy:
stage: deploy
image: alpine:3.21
before_script:
- apk add --no-cache openssh-client
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh && chmod 700 ~/.ssh
- echo "$SSH_HOST_KEY" >> ~/.ssh/known_hosts
script:
- |
ssh $SSH_USER@$SSH_HOST "
docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY &&
docker pull $IMAGE_LATEST &&
docker stop arcline-landing 2>/dev/null || true &&
docker rm arcline-landing 2>/dev/null || true &&
docker run -d \
--name arcline-landing \
--restart unless-stopped \
-p 8080:8080 \
$IMAGE_LATEST
"
only:
- main
needs:
- build

34
Dockerfile Normal file
View File

@@ -0,0 +1,34 @@
# ── Stage 1: Build ─────────────────────────────────────────────────────────
FROM golang:1.26-alpine AS builder
WORKDIR /build
COPY go.* ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="-s -w" -o landing .
# ── Stage 2: Runtime ────────────────────────────────────────────────────────
FROM alpine:3.21 AS runtime
RUN apk add --no-cache ca-certificates && \
addgroup -S arcline && \
adduser -S -G arcline arcline
WORKDIR /app
COPY --from=builder /build/landing ./
COPY --from=builder /build/static ./static
COPY --from=builder /build/templates ./templates
RUN chown -R arcline:arcline /app
USER arcline
ENV PORT=8080
EXPOSE 8080
CMD ["./landing"]

BIN
arcline-landing Executable file

Binary file not shown.

24
docker-compose.yml Normal file
View File

@@ -0,0 +1,24 @@
# Arcline Project — Landing Page
#
# Deploy: docker compose up -d
# Stop: docker compose down
# Logs: docker compose logs -f
# Build: docker compose build
services:
landing:
build:
context: .
dockerfile: Dockerfile
container_name: arcline-landing
restart: unless-stopped
ports:
- "8080:8080"
environment:
- PORT=8080
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080"]
interval: 30s
timeout: 3s
retries: 3
start_period: 10s

3
go.mod Normal file
View File

@@ -0,0 +1,3 @@
module arclineproject.org/landing
go 1.26.4

BIN
landing Executable file

Binary file not shown.

80
main.go Normal file
View File

@@ -0,0 +1,80 @@
package main
import (
"html/template"
"log"
"net/http"
"os"
)
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
// Serve static assets
fs := http.FileServer(http.Dir("static"))
http.Handle("/static/", http.StripPrefix("/static/", fs))
// Page routes
http.HandleFunc("/", handleIndex)
// HTMX partial routes
http.HandleFunc("/partials/waitlist", handleWaitlist)
log.Printf("Arcline Project server starting on :%s", port)
if err := http.ListenAndServe(":"+port, nil); err != nil {
log.Fatalf("server failed: %v", err)
}
}
func handleIndex(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
tmpl, err := template.ParseFiles("templates/base.html", "templates/index.html")
if err != nil {
log.Printf("template error: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
data := map[string]interface{}{
"Title": "Arcline Project",
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
if err := tmpl.ExecuteTemplate(w, "base", data); err != nil {
log.Printf("render error: %v", err)
}
}
func handleWaitlist(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}
email := r.FormValue("email")
// Parse edition preferences (checkboxes with same name)
r.ParseForm()
editions := r.Form["edition"]
log.Printf("Waitlist signup: %s | editions: %v", email, editions)
tmpl, err := template.ParseFiles("templates/partials/waitlist_confirmation.html")
if err != nil {
log.Printf("template error: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
if err := tmpl.Execute(w, map[string]string{"Email": email}); err != nil {
log.Printf("render error: %v", err)
}
}

813
static/css/style.css Normal file
View File

@@ -0,0 +1,813 @@
/* ========================================
Arcline Project — Landing Page
Hand-crafted, not generated.
======================================== */
*,
*::before,
*::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--bg: #0d0e10;
--bg-raised: #15171b;
--bg-card: #1a1c22;
--bg-card-hover: #1f2128;
--border: hsl(225 7% 18%);
--border-hover: hsl(225 8% 26%);
--text: hsl(223 8% 88%);
--text-soft: hsl(223 6% 65%);
--text-faint: hsl(223 5% 41%);
--accent: #0acf97;
--accent-dim: hsl(162 88% 33%);
--accent-bg: hsla(162 88% 43% / 0.09);
--accent-border: hsla(162 88% 43% / 0.18);
--amber: #f09d51;
--amber-bg: hsla(28 84% 63% / 0.07);
--mono: "JetBrains Mono", "Fira Code", "SF Mono", "Cascadia Code", monospace;
--sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--pad-card: 22px;
--w-main: 1040px;
--w-narrow: 680px;
}
html { scroll-behavior: smooth; }
body {
font-family: var(--sans);
background: var(--bg);
color: var(--text);
line-height: 1.65;
-webkit-font-smoothing: antialiased;
}
body::after {
content: "";
position: fixed;
inset: 0;
z-index: 9999;
pointer-events: none;
opacity: 0.022;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
/* Nav */
.nav {
position: sticky;
top: 0;
z-index: 100;
background: hsl(225 6% 6% / 0.88);
backdrop-filter: blur(18px);
-webkit-backdrop-filter: blur(18px);
border-bottom: 1px solid var(--border);
}
.nav-inner {
max-width: var(--w-main);
margin: 0 auto;
padding: 0 28px;
height: 58px;
display: flex;
align-items: center;
justify-content: space-between;
}
.logo {
display: flex;
align-items: center;
gap: 11px;
font-family: var(--mono);
font-size: 1.05rem;
font-weight: 600;
color: var(--text);
text-decoration: none;
letter-spacing: -0.01em;
}
.logo-icon { flex-shrink: 0; }
.nav nav { display: flex; gap: 2px; }
.nav nav a {
color: var(--text-soft);
text-decoration: none;
padding: 7px 14px;
border-radius: 5px;
font-size: 0.825rem;
font-weight: 470;
transition: color 0.18s, background 0.18s;
}
.nav nav a:hover {
color: var(--text);
background: hsl(225 6% 14%);
}
/* Hero */
.hero {
padding: 130px 28px 110px;
text-align: center;
position: relative;
overflow: hidden;
}
.hero::before {
content: "";
position: absolute;
top: -40%;
left: 30%;
width: 960px;
height: 960px;
background: radial-gradient(ellipse at center, hsla(162 88% 43% / 0.075) 0%, transparent 60%);
pointer-events: none;
}
.hero::after {
content: "";
position: absolute;
inset: 0;
pointer-events: none;
opacity: 0.03;
background:
linear-gradient(25deg, transparent 48%, var(--border) 48%, var(--border) 52%, transparent 52%),
linear-gradient(25deg, transparent 48%, var(--border) 48%, var(--border) 52%, transparent 52%);
background-size: 90px 90px;
background-position: 0 0, 45px 45px;
}
.hero-inner {
max-width: var(--w-narrow);
margin: 0 auto;
position: relative;
z-index: 1;
}
.hero-badge {
display: inline-block;
font-family: var(--mono);
font-size: 0.72rem;
color: var(--accent);
background: hsla(162 88% 43% / 0.06);
border: 1px solid var(--accent-border);
padding: 5px 14px;
margin-bottom: 32px;
}
.hero-badge::before {
content: "$ ";
color: var(--text-faint);
}
.hero h1 {
font-size: clamp(2.4rem, 6vw, 4rem);
font-weight: 750;
letter-spacing: -0.035em;
line-height: 1.08;
margin-bottom: 26px;
color: var(--text);
}
.hero-highlight { color: var(--accent); }
.tagline {
font-size: 1.05rem;
color: var(--text-soft);
margin-bottom: 42px;
line-height: 1.7;
max-width: 520px;
margin-left: auto;
margin-right: auto;
}
.hero-actions {
display: flex;
gap: 14px;
justify-content: center;
flex-wrap: wrap;
}
.cta {
display: inline-flex;
align-items: center;
background: var(--accent);
color: hsl(0 0% 8%);
padding: 13px 30px;
font-size: 0.92rem;
font-weight: 620;
text-decoration: none;
transition: background 0.18s, transform 0.14s;
font-family: var(--mono);
letter-spacing: -0.01em;
}
.cta:hover {
background: var(--accent-dim);
transform: translateY(-1px);
}
.cta-secondary {
display: inline-flex;
align-items: center;
color: var(--text-soft);
padding: 13px 30px;
font-size: 0.92rem;
font-weight: 470;
text-decoration: none;
border: 1px solid var(--border);
transition: border-color 0.18s, color 0.18s;
}
.cta-secondary:hover {
border-color: var(--border-hover);
color: var(--text);
}
/* Sections */
.section {
padding: 105px 28px;
position: relative;
}
.section-alt { background: var(--bg-raised); }
.section-inner {
max-width: var(--w-main);
margin: 0 auto;
}
.section-label {
display: block;
font-family: var(--mono);
font-size: 0.7rem;
color: var(--accent);
margin-bottom: 14px;
text-transform: lowercase;
letter-spacing: 0.04em;
}
.section-label::before { content: "// "; color: var(--text-faint); }
.section-inner h2 {
font-size: clamp(1.6rem, 3.6vw, 2.35rem);
font-weight: 680;
margin-bottom: 18px;
letter-spacing: -0.02em;
line-height: 1.22;
color: var(--text);
max-width: 680px;
}
.section-desc {
color: var(--text-soft);
font-size: 1rem;
max-width: 590px;
margin-bottom: 52px;
line-height: 1.7;
}
/* Principles */
.principles-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(302px, 1fr));
gap: 10px;
}
.principle {
display: flex;
gap: 17px;
background: var(--bg-card);
border: 1px solid var(--border);
padding: var(--pad-card);
transition: border-color 0.22s;
position: relative;
overflow: hidden;
}
.principle::after {
content: "";
position: absolute;
inset: -1px;
background: var(--accent-bg);
opacity: 0;
transition: opacity 0.25s;
pointer-events: none;
}
.principle:hover { border-color: var(--accent-border); }
.principle:hover::after { opacity: 1; }
.principle-icon {
flex-shrink: 0;
width: 38px;
height: 38px;
display: flex;
align-items: center;
justify-content: center;
color: var(--accent);
margin-top: 2px;
}
.principle h3 {
font-family: var(--mono);
font-size: 0.83rem;
font-weight: 620;
margin-bottom: 4px;
color: var(--text);
letter-spacing: -0.01em;
}
.principle p {
color: var(--text-soft);
font-size: 0.845rem;
line-height: 1.6;
}
/* Stack Grid */
.stack-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
}
.stack-card {
background: var(--bg-card);
border: 1px solid var(--border);
overflow: hidden;
transition: border-color 0.22s;
position: relative;
}
.stack-card::before {
content: "";
position: absolute;
top: 0; left: 0; bottom: 0;
width: 3px;
transition: width 0.2s;
}
.stack-card:nth-child(1)::before { background: var(--accent); }
.stack-card:nth-child(2)::before { background: hsl(280 45% 55%); }
.stack-card:nth-child(3)::before { background: hsl(200 60% 52%); }
.stack-card:nth-child(4)::before { background: var(--amber); }
.stack-card:hover { border-color: var(--border-hover); }
.stack-card:hover::before { width: 5px; }
.stack-header {
font-family: var(--mono);
font-size: 0.7rem;
font-weight: 650;
text-transform: lowercase;
letter-spacing: 0.05em;
padding: 20px 22px 10px 22px;
color: var(--text-faint);
}
.stack-card:nth-child(1) .stack-header { color: var(--accent); }
.stack-card:nth-child(2) .stack-header { color: hsl(280 45% 65%); }
.stack-card:nth-child(3) .stack-header { color: hsl(200 60% 58%); }
.stack-card:nth-child(4) .stack-header { color: var(--amber); }
.stack-card ul {
list-style: none;
padding: 0 22px 22px 22px;
margin: 0;
}
.stack-card li {
position: relative;
padding: 6px 0 6px 16px;
color: var(--text-soft);
font-size: 0.83rem;
line-height: 1.5;
}
.stack-card li::before {
content: "";
position: absolute;
left: 0;
top: 12px;
width: 5px;
height: 5px;
}
.stack-card:nth-child(1) li::before { background: var(--accent); opacity: 0.55; }
.stack-card:nth-child(2) li::before { background: hsl(280 45% 55%); opacity: 0.55; }
.stack-card:nth-child(3) li::before { background: hsl(200 60% 52%); opacity: 0.55; }
.stack-card:nth-child(4) li::before { background: var(--amber); opacity: 0.55; }
/* Edition Teaser */
.edition-teaser {
margin-top: 30px;
padding: 20px 24px;
border: 1px dashed var(--accent-border);
background: hsla(162 88% 43% / 0.04);
position: relative;
}
.edition-teaser-badge {
display: inline-block;
font-family: var(--mono);
font-size: 0.64rem;
color: var(--accent-dim);
margin-bottom: 8px;
}
.edition-teaser p {
color: var(--text-soft);
font-size: 0.88rem;
line-height: 1.7;
max-width: 700px;
}
.edition-teaser strong { color: var(--text); font-weight: 660; }
/* Tool Grid */
.tool-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(296px, 1fr));
gap: 8px;
}
.tool {
display: flex;
flex-direction: column;
gap: 5px;
background: var(--bg-card);
border: 0 solid var(--accent-border);
border-left-width: 2px;
padding: 19px 22px;
transition: border-color 0.2s, background 0.18s, border-left-width 0.2s;
}
.tool:hover {
border-left-width: 4px;
border-left-color: var(--accent);
background: var(--bg-card-hover);
}
.tool code {
font-family: var(--mono);
font-size: 0.82rem;
font-weight: 580;
color: var(--accent);
letter-spacing: -0.01em;
}
.tool span {
color: var(--text-soft);
font-size: 0.81rem;
line-height: 1.55;
}
/* Compare Grid */
.compare-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
gap: 10px;
}
.compare-card {
background: var(--bg-card);
border: 1px solid var(--border);
padding: 26px 22px;
transition: border-color 0.22s, background 0.2s;
}
.compare-card:hover { border-color: var(--border-hover); }
.compare-highlight {
border-color: var(--accent-border);
background: linear-gradient(155deg, var(--bg-card) 0%, hsla(162 88% 43% / 0.06) 100%);
}
.compare-label {
font-family: var(--mono);
font-size: 0.72rem;
font-weight: 620;
text-transform: lowercase;
letter-spacing: 0.02em;
margin-bottom: 10px;
color: var(--text-faint);
}
.compare-highlight .compare-label { color: var(--accent); }
.compare-desc {
color: var(--text-soft);
font-size: 0.88rem;
line-height: 1.6;
}
/* Contribute Grid */
.contribute-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
gap: 10px;
}
.contribute-card {
display: flex;
flex-direction: column;
gap: 10px;
background: var(--bg-card);
border: 1px solid var(--border);
padding: 28px 22px;
color: var(--text);
text-decoration: none;
transition: border-color 0.22s, background 0.2s;
position: relative;
}
.contribute-card::after {
content: " →";
position: absolute;
right: 22px;
top: 28px;
font-family: var(--mono);
color: var(--accent);
opacity: 0;
transform: translateX(-8px);
transition: opacity 0.2s, transform 0.2s;
}
.contribute-card:hover { border-color: var(--accent-border); }
.contribute-card:hover::after { opacity: 1; transform: translateX(0); }
.contribute-card span {
font-size: 0.96rem;
font-weight: 640;
letter-spacing: -0.01em;
}
.contribute-card small {
color: var(--text-soft);
font-size: 0.82rem;
line-height: 1.5;
}
.contribute-icon {
width: 34px;
height: 34px;
display: flex;
align-items: center;
justify-content: center;
background: hsla(162 88% 43% / 0.07);
margin-bottom: 2px;
}
/* Inline Link */
.inline-link {
color: var(--accent);
text-decoration: none;
border-bottom: 1px solid var(--accent-border);
padding-bottom: 1px;
transition: border-color 0.18s;
}
.inline-link:hover { border-bottom-color: var(--accent); }
/* Sponsor */
.sponsor-section .section-inner { text-align: left; }
.sponsor-content {
display: flex;
flex-direction: column;
gap: 22px;
max-width: 620px;
}
.sponsor-desc {
max-width: 600px;
color: var(--text-soft);
font-size: 0.94rem;
line-height: 1.7;
}
.sponsor-desc strong { color: var(--text); font-weight: 660; }
.sponsor-tagline {
font-family: var(--mono);
font-size: 0.78rem;
color: var(--text-faint);
padding: 8px 0;
}
.sponsor-tagline::before { content: "# "; color: var(--border-hover); }
/* Waitlist */
.waitlist-form {
display: flex;
flex-direction: column;
gap: 18px;
margin-top: 6px;
max-width: 480px;
}
.waitlist-form .input,
.waitlist-form .btn {
width: 100%;
}
/* Edition Checkboxes */
.edition-checkboxes {
border: none;
padding: 0;
display: flex;
gap: 22px;
}
.edition-checkboxes legend {
font-size: 0.83rem;
color: var(--text-soft);
margin-bottom: 10px;
padding: 0;
float: left;
width: 100%;
}
.edition-checkbox {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
font-size: 0.88rem;
color: var(--text-soft);
user-select: none;
transition: color 0.18s;
}
.edition-checkbox:hover { color: var(--text); }
.edition-checkbox input[type="checkbox"] {
appearance: none;
-webkit-appearance: none;
width: 18px;
height: 18px;
border: 2px solid var(--border);
background: var(--bg-card);
cursor: pointer;
position: relative;
flex-shrink: 0;
transition: border-color 0.18s, background 0.18s;
}
.edition-checkbox input[type="checkbox"]:checked {
border-color: var(--accent);
background: var(--accent);
}
.edition-checkbox input[type="checkbox"]:checked::after {
content: "";
position: absolute;
left: 4px;
top: 2px;
width: 5px;
height: 9px;
border: solid hsl(0 0% 8%);
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
/* Waitlist Form Row */
.waitlist-row {
display: flex;
gap: 11px;
}
.input {
padding: 13px 18px;
border: 1px solid var(--border);
border-radius: 3px;
background: var(--bg-card);
color: var(--text);
font-size: 0.9rem;
min-width: 310px;
outline: none;
transition: border-color 0.18s, box-shadow 0.18s;
font-family: var(--mono);
}
.input::placeholder { color: var(--text-faint); font-family: var(--mono); }
.input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-bg);
}
.btn {
padding: 13px 30px;
border: none;
border-radius: 3px;
background: var(--accent);
color: hsl(0 0% 8%);
font-size: 0.9rem;
font-weight: 620;
cursor: pointer;
transition: background 0.18s;
font-family: var(--mono);
letter-spacing: -0.01em;
}
.btn:hover { background: var(--accent-dim); }
.confirmation {
margin-top: 18px;
padding: 12px 20px;
color: var(--accent);
background: var(--accent-bg);
border: 1px solid var(--accent-border);
font-weight: 520;
font-size: 0.88rem;
display: inline-block;
font-family: var(--mono);
}
.waitlist-note {
margin-top: 22px;
font-size: 0.8rem;
color: var(--text-faint);
}
/* Footer */
.footer {
padding: 44px 28px 30px;
border-top: 1px solid var(--border);
}
.footer-inner {
max-width: var(--w-main);
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: flex-start;
flex-wrap: wrap;
gap: 22px;
}
.footer-brand p {
color: var(--text-soft);
font-size: 0.84rem;
margin-top: 5px;
}
.footer-logo {
font-family: var(--mono);
font-size: 1rem;
font-weight: 620;
color: var(--text);
text-decoration: none;
}
.footer-links { display: flex; gap: 22px; }
.footer-links a {
color: var(--text-soft);
text-decoration: none;
font-size: 0.84rem;
transition: color 0.18s;
}
.footer-links a:hover { color: var(--text); }
.footer-bottom {
max-width: var(--w-main);
margin: 28px auto 0;
padding-top: 18px;
border-top: 1px solid var(--border);
text-align: left;
}
.footer-bottom p {
font-family: var(--mono);
color: var(--text-faint);
font-size: 0.74rem;
}
/* Responsive */
@media (max-width: 920px) {
.stack-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 768px) {
.hero { padding: 90px 22px 72px; }
.section { padding: 68px 22px; }
.hero-actions { flex-direction: column; align-items: stretch; }
.cta, .cta-secondary { justify-content: center; text-align: center; }
.principles-grid { grid-template-columns: 1fr; }
.tool-grid { grid-template-columns: 1fr; }
}
@media (max-width: 580px) {
.hero { padding: 62px 18px 50px; }
.hero h1 { font-size: 1.85rem; }
.section { padding: 50px 18px; }
.section-inner h2 { font-size: 1.4rem; }
.nav nav a { padding: 7px 9px; font-size: 0.76rem; }
.input { min-width: 100%; }
.btn { width: 100%; }
.stack-grid { grid-template-columns: 1fr; }
.compare-grid { grid-template-columns: 1fr; }
.contribute-grid { grid-template-columns: 1fr; }
.footer-inner { flex-direction: column; }
}

6
static/favicon.svg Normal file
View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
<rect width="32" height="32" rx="6" ry="6" fill="#0d0e10"/>
<path d="M5 27L16 5L27 27" fill="none" stroke="#0acf97" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 20H23" fill="none" stroke="#0acf97" stroke-width="2.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 374 B

1
static/js/htmx.min.js vendored Normal file

File diff suppressed because one or more lines are too long

17
templates/base.html Normal file
View File

@@ -0,0 +1,17 @@
{{define "base"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{.Title}}</title>
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
<link rel="stylesheet" href="/static/css/style.css">
<script src="/static/js/htmx.min.js"></script>
</head>
<body>
{{template "content" .}}
</body>
</html>
{{end}}

400
templates/index.html Normal file
View File

@@ -0,0 +1,400 @@
{{define "content"}}
<header class="nav">
<div class="nav-inner">
<a href="/" class="logo">
<svg class="logo-icon" width="28" height="28" viewBox="0 0 32 32" fill="none">
<rect width="32" height="32" rx="6" fill="#0d0e10"/>
<path d="M5 27L16 5L27 27" fill="none" stroke="#0acf97" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 20H23" fill="none" stroke="#0acf97" stroke-width="2.5" stroke-linecap="round"/>
</svg>
<span>Arcline Project</span>
</a>
<nav>
<a href="#about">About</a>
<a href="#toolchain">Toolchain</a>
<a href="#contribute">Contribute</a>
<a href="#get-started">Get Started</a>
</nav>
</div>
</header>
<main>
<!-- Hero -->
<section class="hero">
<div class="hero-inner">
<h1>
The Linux OS for<br>
<span class="hero-highlight">people who run infrastructure.</span>
</h1>
<p class="tagline">
Arcline OS is a hardened, privacy-first operating system built on Debian.
Ships pre-configured with monitoring, auditing, and security tooling —
so your systems are production-ready from first boot.
</p>
<div class="hero-actions">
<a href="#get-started" class="cta">Get Early Access</a>
<a href="#about" class="cta-secondary">Learn more</a>
</div>
</div>
</section>
<!-- What is Arcline OS -->
<section id="about" class="section section-alt">
<div class="section-inner">
<div class="section-label">what is arcline os?</div>
<h2>A Debian-derived OS that doesn't<br>make you start from scratch.</h2>
<p class="section-desc">
Debian gives you a rock-solid base. Arcline OS hardens it, layers on a
full observability stack, and ships with pre-configured container
infrastructure. No telemetry. No snap store. No cloud integration.
Just a secure, auditable OS that respects your hardware.
</p>
<div class="principles-grid">
<div class="principle">
<div class="principle-icon">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="3" width="18" height="18" rx="3"/>
<path d="M8 12l2.5 2.5L16 9"/>
</svg>
</div>
<div>
<h3>Secure by default</h3>
<p>Hardened Linux kernel with seccomp, AppArmor profiles, and restrictive nftables firewall. You opt in to exposure — never out.</p>
</div>
</div>
<div class="principle">
<div class="principle-icon">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"/>
<path d="M12 6v6l4 2"/>
</svg>
</div>
<div>
<h3>Privacy-first</h3>
<p>Zero telemetry. Zero analytics. Zero cloud integration. No network calls you didn't initiate.</p>
</div>
</div>
<div class="principle">
<div class="principle-icon">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="2" y="2" width="20" height="20" rx="3"/>
<rect x="6" y="6" width="12" height="12" rx="1.5" stroke-dasharray="3 2"/>
</svg>
</div>
<div>
<h3>btrfs-native</h3>
<p>btrfs root with subvolume snapshots, transparent compression, and boot-to-snapshot rollback — not bolted on, built in.</p>
</div>
</div>
<div class="principle">
<div class="principle-icon">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 7h16v12H4V7z"/>
<path d="M8 3v4"/>
<path d="M16 3v4"/>
<path d="M4 13h16" stroke-dasharray="2 1"/>
</svg>
</div>
<div>
<h3>Developer-ready</h3>
<p>Go, Rust, Python, Node.js toolchains pre-configured. Git, Make, LLVM/Clang included. Start building immediately.</p>
</div>
</div>
<div class="principle">
<div class="principle-icon">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="3"/>
<path d="M12 2L22 8.5v7L12 22 2 15.5v-7L12 2z"/>
</svg>
</div>
<div>
<h3>Infrastructure-aware</h3>
<p>Prometheus node_exporter, Grafana dashboards, and Loki log aggregation — pre-configured out of the box.</p>
</div>
</div>
<div class="principle">
<div class="principle-icon">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 4h8l4 4v12H4V4z"/>
<path d="M12 4v4h4"/>
<line x1="8" y1="12" x2="16" y2="12" stroke-opacity="0.5"/>
<line x1="8" y1="15" x2="13" y2="15" stroke-opacity="0.5"/>
</svg>
</div>
<div>
<h3>Fully documented</h3>
<p>Every feature and tool includes offline man pages and a hardening guide. You own the system — and you understand it.</p>
</div>
</div>
</div>
</div>
</section>
<!-- What's Included -->
<section class="section">
<div class="section-inner">
<div class="section-label">what's included</div>
<h2>Production-ready from first boot.</h2>
<p class="section-desc">
Arcline OS layers pre-configured infrastructure tooling on top of a hardened
Debian base. Everything is documented, auditable, and yours.
</p>
<div class="stack-grid">
<div class="stack-card">
<div class="stack-header">Base System</div>
<ul>
<li>Debian stable (Bookworm)</li>
<li>Hardened Linux kernel (lockdown, seccomp)</li>
<li>btrfs root + snapshot rollback</li>
<li>nftables firewall (default-deny)</li>
<li>Docker + Podman containers</li>
</ul>
</div>
<div class="stack-card">
<div class="stack-header">Arcline Toolchain</div>
<ul>
<li>arcline-uptime — service monitoring</li>
<li>arcline-check — privacy auditor</li>
<li>arcline-audit — site health scanner</li>
<li>arcline-dns — propagation checker</li>
<li>arcline-vault — secrets store</li>
<li>arcline-email — self-hosted mail stack</li>
</ul>
</div>
<div class="stack-card">
<div class="stack-header">Developer Environment</div>
<ul>
<li>Go, Rust, Python, Node.js</li>
<li>Git, Make, CMake, LLVM/Clang</li>
<li>Docker / Podman pre-configured</li>
<li>VS Code / Helix / Vim configs</li>
</ul>
</div>
<div class="stack-card">
<div class="stack-header">Observability</div>
<ul>
<li>Prometheus node_exporter</li>
<li>Grafana dashboards</li>
<li>Loki log aggregation</li>
<li>Pre-built alerting rules</li>
</ul>
</div>
</div>
<div class="edition-teaser">
<div class="edition-teaser-badge">// also in the works</div>
<p>
<strong>Arcline Workstation</strong> — same hardened base, with a lightweight KDE Plasma desktop,
pre-configured dev toolchains, and privacy-hardened browser profiles.
One ISO. Two targets. Pick yours at install time.
</p>
</div>
</div>
</section>
<!-- Toolchain deep-dive -->
<section id="toolchain" class="section section-alt">
<div class="section-inner">
<div class="section-label">the toolchain</div>
<h2>11 Go tools. Built in production.<br>Yours to use.</h2>
<p class="section-desc">
Every Arcline tool was built to solve real infrastructure problems.
They run in production at Arcline IT. Now they ship with Arcline OS.
</p>
<div class="tool-grid">
<div class="tool">
<code>arcline-uptime</code>
<span>HTTP, TCP, TLS, DNS monitoring with Prometheus metrics export</span>
</div>
<div class="tool">
<code>arcline-check</code>
<span>CDN and transparency auditor — verifies sites aren't leaking data</span>
</div>
<div class="tool">
<code>arcline-audit</code>
<span>Full site health scanner — SSL, headers, performance, accessibility</span>
</div>
<div class="tool">
<code>arcline-dns</code>
<span>DNS propagation checker across multiple resolvers</span>
</div>
<div class="tool">
<code>arcline-vault</code>
<span>Encrypted secrets store with REST API and CLI</span>
</div>
<div class="tool">
<code>arcline-email</code>
<span>Self-hosted SMTP/IMAP stack — OpenSMTPD + Dovecot + Rspamd</span>
</div>
<div class="tool">
<code>arcline-migrate</code>
<span>cPanel and Plesk to Arcline migration tool</span>
</div>
<div class="tool">
<code>arcline-billing</code>
<span>Stripe-powered subscription management</span>
</div>
<div class="tool">
<code>arcline-portal</code>
<span>Customer dashboard with SSL monitoring and ticketing</span>
</div>
<div class="tool">
<code>arcline-website</code>
<span>Go HTTP server powering arcline.it</span>
</div>
<div class="tool">
<code>arcline-status</code>
<span>Static status page generator</span>
</div>
</div>
</div>
</section>
<!-- Why Arcline OS -->
<section class="section">
<div class="section-inner">
<div class="section-label">why arcline os?</div>
<h2>No major Linux distro ships<br>pre-hardened for production.</h2>
<p class="section-desc">
Debian gives you the packages. Arcline OS assembles them into a finished
system. A privacy-first, zero-telemetry operating system with built-in
monitoring, auditing, and security — configured the way you'd do it
yourself, if you had the weekend.
</p>
<div class="compare-grid">
<div class="compare-card">
<div class="compare-label">Ubuntu Server</div>
<div class="compare-desc">Great ecosystem and hardware support — but Snap packages and cloud integration are baked in by default.</div>
</div>
<div class="compare-card">
<div class="compare-label">Fedora Server</div>
<div class="compare-desc">Cutting-edge kernel and tooling — but rapid release cycles mean more churn on production machines.</div>
</div>
<div class="compare-card">
<div class="compare-label">Alpine Linux</div>
<div class="compare-desc">Minimal and musl-based — ideal for containers, but limited as a full-featured server workstation.</div>
</div>
<div class="compare-card compare-highlight">
<div class="compare-label">Arcline OS</div>
<div class="compare-desc">Hardened Debian base. Pre-configured toolchain. Built-in observability. Zero telemetry. Ready to deploy.</div>
</div>
</div>
</div>
</section>
<!-- Contribute -->
<section id="contribute" class="section section-alt">
<div class="section-inner">
<div class="section-label">community</div>
<h2>Built in the open. Shaped by the<br>people who use it.</h2>
<p class="section-desc">
Arcline OS is open-source under the GPL. The code lives on
<a href="https://git.arcline.it" class="inline-link">git.arcline.it</a>.
Every tool, every config, every build script — auditable and forkable.
</p>
<div class="contribute-grid">
<a href="https://git.arcline.it" class="contribute-card">
<div class="contribute-icon">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 00-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0020 4.77 5.07 5.07 0 0019.91 1S18.73.65 15.23 2.5a13.38 13.38 0 00-7 0C5.27.65 4.09 1 4.09 1A5.07 5.07 0 004 4.77a5.44 5.44 0 00-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 008 18.13V22"/>
</svg>
</div>
<span>Browse the source</span>
<small>All 11 tools, build scripts, and infrastructure code.</small>
</a>
<a href="https://git.arcline.it" class="contribute-card">
<div class="contribute-icon">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/>
<polyline points="14 2 14 8 20 8"/>
<line x1="16" y1="13" x2="8" y2="13"/>
<line x1="16" y1="17" x2="8" y2="17"/>
</svg>
</div>
<span>Read the docs</span>
<small>Hardening guides, install docs, and infrastructure cookbook.</small>
</a>
<a href="https://git.arcline.it" class="contribute-card">
<div class="contribute-icon">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"/>
<path d="M12 6v6l4 2"/>
</svg>
</div>
<span>Contribute</span>
<small>Submit patches, report issues, or join the discussion.</small>
</a>
</div>
</div>
</section>
<!-- Get Started -->
<section id="get-started" class="section">
<div class="section-inner">
<div class="section-label">get started</div>
<h2>Arcline OS is in active development.<br>Be there at the start.</h2>
<p class="section-desc">
We're packaging the tools, building the ISO pipeline, and hardening the defaults.
Sign up to receive early access and help shape the first release.
</p>
<form
hx-post="/partials/waitlist"
hx-target="#waitlist-result"
hx-swap="outerHTML"
class="waitlist-form"
>
<fieldset class="edition-checkboxes">
<legend>I'm interested in:</legend>
<label class="edition-checkbox">
<input type="checkbox" name="edition" value="server" checked>
<span>Server</span>
</label>
<label class="edition-checkbox">
<input type="checkbox" name="edition" value="workstation">
<span>Workstation</span>
</label>
</fieldset>
<input
type="email"
name="email"
placeholder="you@example.com"
required
class="input"
>
<button type="submit" class="btn">Get Early Access</button>
</form>
<div id="waitlist-result"></div>
<p class="waitlist-note">No spam. No tracking. We'll only email you when there's something to try.</p>
</div>
</section>
<!-- Sponsor -->
<section class="section section-alt sponsor-section">
<div class="section-inner">
<div class="section-label">sponsored by</div>
<div class="sponsor-content">
<div class="sponsor-desc">
<p>The Arcline Project is funded and operated by <strong>Arcline IT LLC</strong> — the same model as Red Hat and Fedora. The commercial services business at <a href="https://arcline.it" class="inline-link">arcline.it</a> keeps the lights on so the OS stays independent and sustainable.</p>
</div>
<div class="sponsor-tagline">Secure by default. Self-hosted by principle.</div>
</div>
</div>
</section>
</main>
<footer class="footer">
<div class="footer-inner">
<div class="footer-brand">
<a href="/" class="footer-logo">Arcline Project</a>
<p>An open-source OS for people who run infrastructure.</p>
</div>
<div class="footer-links">
<a href="https://arcline.it">Arcline IT</a>
<a href="https://git.arcline.it">Git</a>
</div>
</div>
<div class="footer-bottom">
<p>&copy; 2026 Arcline IT LLC. GPL Licensed. No telemetry. No tracking.</p>
</div>
</footer>
{{end}}

View File

@@ -0,0 +1,2 @@
<p class="confirmation">Thanks {{.Email}}! We'll be in touch.</p>