carlos / carloku Public

flagship redesign: canon app-platform landing + restyled get-started

Category-standard direction chosen by Paul over the rolled alternatives
(impeccable seed 5f64f4cf): sticky nav, terminal-demo hero typing the real
carlos ship/promote/rollback output, workflow steps, feature cards,
open-source band, closing CTA. Self-hosted Geist/Geist Mono (OFL), one
small hand-written site.js (terminal typing + header hairline), both
color schemes, no build step, no external requests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell pushed by claude agent, operated by paul@keymail.dev 2e6889aa4e985b132fb784a912f863302208cc8c parent 01b5365
7 files changed, +685 −116
  • fonts/Geist-Variable.woff2 binary
  • fonts/GeistMono-Variable.woff2 binary
  • fonts/LICENSE-OFL.txt +92 −0
  • get-started/index.html +32 −12
  • index.html +147 −42
  • site.css +358 −62
  • site.js +56 −0
diff --git a/fonts/Geist-Variable.woff2 b/fonts/Geist-Variable.woff2
new file mode 100644
index 0000000..5c5999d
Binary files /dev/null and b/fonts/Geist-Variable.woff2 differ
diff --git a/fonts/GeistMono-Variable.woff2 b/fonts/GeistMono-Variable.woff2
new file mode 100644
index 0000000..efaf95d
Binary files /dev/null and b/fonts/GeistMono-Variable.woff2 differ
diff --git a/fonts/LICENSE-OFL.txt b/fonts/LICENSE-OFL.txt
new file mode 100644
index 0000000..8d003fe
--- /dev/null
+++ b/fonts/LICENSE-OFL.txt
@@ -0,0 +1,92 @@
+Copyright (c) 2023 Vercel, in collaboration with basement.studio
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION AND CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/get-started/index.html b/get-started/index.html
index d3e22ef..521c609 100644
--- a/get-started/index.html
+++ b/get-started/index.html
@@ -10,19 +10,35 @@
<meta property="og:type" content="website">
<meta property="og:url" content="https://carloku.com/get-started/">
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
-<meta name="theme-color" content="#16141c" media="(prefers-color-scheme: dark)">
+<meta name="theme-color" content="#0e0c13" media="(prefers-color-scheme: dark)">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
+<link rel="preload" href="/fonts/Geist-Variable.woff2" as="font" type="font/woff2" crossorigin>
+<link rel="preload" href="/fonts/GeistMono-Variable.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="/site.css">
</head>
<body>
-<header class="hero">
- <p class="eyebrow"><a href="/">πŸ€– CARLOKU</a></p>
- <h1>Get started<span class="dot">.</span></h1>
- <p class="lede">This is the head half; the heart half is that you'll be live
- before it feels like work. Four steps, one small tool.</p>
+<a class="skip" href="#main">Skip to content</a>
+<header class="site-head">
+ <div class="wrap">
+ <a class="brand" href="/"><span class="mark">πŸ€–</span>Carloku</a>
+ <nav aria-label="Site">
+ <a href="/#workflow">How it works</a>
+ <a href="/#open">Open source</a>
+ <a href="/get-started/">Get started</a>
+ <a class="head-cta" href="https://dashboard.carloku.com">Dashboard</a>
+ </nav>
+ </div>
</header>
-<main>
- <article>
+<main id="main">
+ <div class="page-hero">
+ <div class="wrap narrow">
+ <h1>Get started<span class="dot">.</span></h1>
+ <p class="lede">This is the head half; the heart half is that you'll be live
+ before it feels like work. Four steps, one small tool.</p>
+ </div>
+ </div>
+ <div class="wrap narrow">
+ <article class="guide">
<section>
<h2>1 Β· Install the CLI</h2>
<pre><code>brew install carlosframework/tap/carlos</code></pre>
@@ -76,12 +92,16 @@ carlos ship -app hello -kind static -version v1 ./public
<a href="https://carlosframework.com">carlosframework.com</a>.</p>
</section>
</article>
+ </div>
</main>
<footer>
- <p>πŸ€– Written by an LLM (Claude), on the ideas, instruction, and editing of humans.
- AI-written text here is always marked and always disclosed; see
- <a href="https://11factor.org/#x">factor X</a>.</p>
- <p><a href="/">← carloku.com</a> Β· <a href="https://github.com/carlosframework/carloku">Source on GitHub</a>.</p>
+ <div class="wrap">
+ <p>πŸ€– Written by an LLM (Claude), on the ideas, instruction, and editing of humans.
+ AI-written text here is always marked and always disclosed; see
+ <a href="https://11factor.org/#x">factor X</a>.</p>
+ <p><a href="/">← carloku.com</a> Β· <a href="https://github.com/carlosframework/carloku">Source on GitHub</a>.</p>
+ </div>
</footer>
+<script src="/site.js" defer></script>
</body>
</html>
diff --git a/index.html b/index.html
index a270228..b43b17f 100644
--- a/index.html
+++ b/index.html
@@ -10,58 +10,163 @@
<meta property="og:type" content="website">
<meta property="og:url" content="https://carloku.com/">
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
-<meta name="theme-color" content="#16141c" media="(prefers-color-scheme: dark)">
+<meta name="theme-color" content="#0e0c13" media="(prefers-color-scheme: dark)">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
+<link rel="preload" href="/fonts/Geist-Variable.woff2" as="font" type="font/woff2" crossorigin>
+<link rel="preload" href="/fonts/GeistMono-Variable.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="/site.css">
</head>
<body>
-<header class="hero">
- <p class="eyebrow">πŸ€– CARLOKU</p>
- <h1>Ship it. Promote it. Sleep<span class="dot">.</span></h1>
- <p class="lede">Carloku is the hosted CARLOS platform: hosting small enough to
- hold in your head. You ship, you look at it, you say go. The rest β€” certificates,
- passwords, servers idling through the night β€” stops being your job.</p>
- <div class="cta">
- <a class="primary" href="/get-started/">Get started</a>
- <a class="secondary" href="https://dashboard.carloku.com">Open the dashboard</a>
+<!--
+THESIS: The category-standard app-platform page, played straight at Heroku/Fly/Render/Railway
+craft β€” chosen by the user as the standing exit over the assigned direction. It refuses
+nothing structurally; it must out-finish, not out-weird.
+OWN-WORLD: Near-black-with-purple dark scheme / white-with-purple light scheme, one accent
+(Carloku purple), Geist + Geist Mono, hairline borders, one dark terminal that stays dark in
+both schemes, one deep-purple full-bleed band.
+STORY: A developer sees the three-command workflow proven in a real terminal, believes
+hosting can be held in one head, and clicks Get started.
+FIRST VIEWPORT: Sticky nav; left, "Ship it. Promote it. Sleep." + lede + Get started primary;
+right, a terminal typing the real ship→promote→rollback session.
+FORM: Canon standing exit, user-chosen over grounded candidate 5; seed key 5f64f4cf.
+FINISH: unreviewed and undocumented is unfinished; this build ends with the finish review,
+the verdict, and DESIGN.md.
+-->
+<a class="skip" href="#main">Skip to content</a>
+<header class="site-head">
+ <div class="wrap">
+ <a class="brand" href="/"><span class="mark">πŸ€–</span>Carloku</a>
+ <nav aria-label="Site">
+ <a href="#workflow">How it works</a>
+ <a href="#open">Open source</a>
+ <a href="/get-started/">Get started</a>
+ <a class="head-cta" href="https://dashboard.carloku.com">Dashboard</a>
+ </nav>
</div>
</header>
-<main>
- <section>
- <h2>The whole workflow</h2>
- <ol class="ship" role="list">
- <li><strong><code>carlos ship</code></strong>. Your app goes up, and you get a
- private link to the real, running thing. Look at it. Poke it. It's live, just
- not public yet.</li>
- <li><strong><code>carlos promote</code></strong>. The exact thing you just
- looked at becomes the public thing. Nothing is rebuilt behind your back; what
- you approved is what people get.</li>
- <li><strong>Changed your mind?</strong> Point at yesterday's version and promote
- that instead. Going back is the same small motion as going forward.</li>
- </ol>
+<main id="main">
+ <section class="hero">
+ <div class="wrap">
+ <div>
+ <h1><span class="nobr">Ship it.</span> <span class="nobr">Promote it.</span> <span class="nobr">Sleep<span class="dot">.</span></span></h1>
+ <p class="lede">Carloku is the hosted CARLOS platform: hosting small enough to
+ hold in your head. You ship, you look at it, you say go. The rest β€” certificates,
+ passwords, servers idling through the night β€” stops being your job.</p>
+ <div class="cta">
+ <a class="btn btn-primary" href="/get-started/">Get started</a>
+ <a class="btn btn-quiet" href="https://dashboard.carloku.com">Open the dashboard</a>
+ </div>
+ <p class="hero-note">Open source underneath β€” hosted here, never locked here.</p>
+ </div>
+ <div class="term" role="img" aria-label="A terminal session: carlos ship uploads the app and prints shipped; carlos promote puts that exact version live; promoting yesterday's version rolls back.">
+ <div class="term-bar" aria-hidden="true"><i></i><i></i><i></i><span>you@laptop β€” carlos</span></div>
+ <div class="term-body" aria-hidden="true">
+ <span class="line cmd" data-type>carlos ship -app hello -kind static -version v4 ./public</span>
+ <span class="line" data-out>using https://dashboard.carloku.com as you@keymail.dev</span>
+ <span class="line ok" data-out>shipped hello v4</span>
+ <span class="line cmt" data-type># look at it privately, then say go:</span>
+ <span class="line cmd" data-type>carlos promote -app hello v4 stable</span>
+ <span class="line ok" data-out>promoted hello v4 -&gt; stable</span>
+ <span class="line cmt" data-type># yesterday was better?</span>
+ <span class="line cmd" data-type>carlos promote -app hello v3 stable</span>
+ <span class="line ok" data-out>promoted hello v3 -&gt; stable</span>
+ <span class="line"><span class="term-caret"></span></span>
+ </div>
+ </div>
+ </div>
</section>
- <section>
- <h2>What you stop thinking about</h2>
- <ul class="features" role="list">
- <li><strong>Certificates</strong> <span>point your domain at Carloku and the
- padlock appears. Renewals happen without you.</span></li>
- <li><strong>Passwords</strong> <span>sign in with a passkey, on the dashboard
- and in the terminal. There's nothing to invent, reuse, or leak.</span></li>
- <li><strong>Idle servers</strong> <span>apps doze when nobody's around and wake
- the instant someone shows up. A quiet app is just quiet.</span></li>
- <li><strong>What's really running</strong> <span>the platform under Carloku is
- open source. You can read every moving part β€” or run it yourself. Hosted here,
- never locked here.</span></li>
- </ul>
+ <section id="workflow">
+ <div class="wrap">
+ <h2>The whole workflow</h2>
+ <p class="section-lede">Three small motions. No pipelines to babysit, no containers
+ to wrap your app in β€” you ship the thing itself.</p>
+ <ol class="steps">
+ <li>
+ <h3><code>carlos ship</code></h3>
+ <p>Your app goes up, and you get a private link to the real, running thing.
+ Look at it. Poke it. It's live, just not public yet.</p>
+ </li>
+ <li>
+ <h3><code>carlos promote</code></h3>
+ <p>The exact thing you just looked at becomes the public thing. Nothing is
+ rebuilt behind your back; what you approved is what people get.</p>
+ </li>
+ <li>
+ <h3>Changed your mind?</h3>
+ <p>Point at yesterday's version and promote that instead. Going back is the
+ same small motion as going forward.</p>
+ </li>
+ </ol>
+ </div>
+ </section>
+ <section class="tint">
+ <div class="wrap">
+ <h2>What you stop thinking about</h2>
+ <ul class="features">
+ <li>
+ <div class="icon" aria-hidden="true">
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
+ </div>
+ <h3>Certificates</h3>
+ <p>Point your domain at Carloku and the padlock appears. Renewals happen
+ without you.</p>
+ </li>
+ <li>
+ <div class="icon" aria-hidden="true">
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="7.5" cy="15.5" r="5.5"/><path d="m21 2-9.6 9.6"/><path d="m15.5 7.5 3 3L22 7l-3-3"/></svg>
+ </div>
+ <h3>Passwords</h3>
+ <p>Sign in with a passkey, on the dashboard and in the terminal. There's
+ nothing to invent, reuse, or leak.</p>
+ </li>
+ <li>
+ <div class="icon" aria-hidden="true">
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/></svg>
+ </div>
+ <h3>Idle servers</h3>
+ <p>Apps doze when nobody's around and wake the instant someone shows up.
+ A quiet app is just quiet.</p>
+ </li>
+ </ul>
+ </div>
+ </section>
+ <section class="band" id="open">
+ <div class="wrap">
+ <h2>Hosted here, never locked here.</h2>
+ <p>The platform under Carloku is open source: CARLOS. You can read every moving
+ part β€” or run the whole thing yourself, on your own machines, and your apps
+ wouldn't know the difference.</p>
+ <p>And what you ship is your app as itself β€” a folder of files, a single binary β€”
+ not a container wrapped around it.</p>
+ <div class="cta">
+ <a class="btn btn-primary" href="https://carlosframework.com">Read the design</a>
+ <a class="btn btn-quiet" href="https://github.com/carlosframework">CARLOS on GitHub</a>
+ </div>
+ <p class="proof">Proof it works: this site is itself a CARLOS app, shipped and
+ promoted the same way you just read about. <a href="https://github.com/carlosframework/carloku">Here's its source.</a></p>
+ </div>
+ </section>
+ <section class="close-cta">
+ <div class="wrap">
+ <h2>Be live before it feels like work<span class="dot">.</span></h2>
+ <p>One small tool, four steps, and your app is on the internet.</p>
+ <div class="cta">
+ <a class="btn btn-primary" href="/get-started/">Get started</a>
+ <a class="btn btn-quiet" href="https://dashboard.carloku.com">Open the dashboard</a>
+ </div>
+ </div>
</section>
</main>
<footer>
- <p>πŸ€– Written by an LLM (Claude), on the ideas, instruction, and editing of humans.
- AI-written text here is always marked and always disclosed; see
- <a href="https://11factor.org/#x">factor X</a>.</p>
- <p>Carloku runs on <a href="https://carlosframework.com">CARLOS</a>: the framework
- is open, and this site is itself a CARLOS app, shipped with <code>carlos ship</code>.</p>
- <p><a href="https://github.com/carlosframework/carloku">Source for this site on GitHub</a>.</p>
+ <div class="wrap">
+ <p>πŸ€– Written by an LLM (Claude), on the ideas, instruction, and editing of humans.
+ AI-written text here is always marked and always disclosed; see
+ <a href="https://11factor.org/#x">factor X</a>.</p>
+ <p>Carloku runs on <a href="https://carlosframework.com">CARLOS</a>: the framework
+ is open, and this site is itself a CARLOS app, shipped with <code>carlos ship</code>.</p>
+ <p><a href="https://github.com/carlosframework/carloku">Source for this site on GitHub</a>.</p>
+ </div>
</footer>
+<script src="/site.js" defer></script>
</body>
</html>
diff --git a/site.css b/site.css
index 8bb4206..5c65016 100644
--- a/site.css
+++ b/site.css
@@ -1,77 +1,373 @@
-/* πŸ€– Carloku shared stylesheet β€” both pages, zero dependencies. */
+/* πŸ€– Carloku shared stylesheet β€” both pages, zero dependencies, no build step.
+ Fonts are Geist / Geist Mono (SIL OFL, see /fonts/LICENSE-OFL.txt), self-hosted. */
+
+@font-face {
+ font-family: "Geist";
+ src: url("/fonts/Geist-Variable.woff2") format("woff2");
+ font-weight: 100 900;
+ font-display: swap;
+}
+@font-face {
+ font-family: "Geist Mono";
+ src: url("/fonts/GeistMono-Variable.woff2") format("woff2");
+ font-weight: 100 900;
+ font-display: swap;
+}
+
:root {
--bg: #ffffff;
- --ink: #201d29;
- --muted: #6f6480;
+ --bg-tint: #f8f7fb;
+ --panel: #ffffff;
+ --ink: #1d1927;
+ --muted: #5d5570;
+ --faint: #6e6880;
--accent: #5b3e96;
- --accent-strong: #4c3380;
- --accent-soft: rgba(91, 62, 150, 0.09);
- --rule: #eae7f0;
- --max: 42rem;
- --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
- --sans: ui-sans-serif, system-ui, -apple-system, sans-serif;
+ --accent-ink: #4c3380;
+ --accent-soft: rgba(91, 62, 150, 0.08);
+ --btn-bg: #5b3e96;
+ --btn-bg-hover: #4c3380;
+ --btn-ink: #ffffff;
+ --line: rgba(29, 25, 39, 0.1);
+ --line-strong: rgba(29, 25, 39, 0.16);
+ --band-bg: #221a35;
+ --band-ink: #f2eef9;
+ --band-muted: #b9aed6;
+ --band-line: rgba(242, 238, 249, 0.14);
+ --term-bg: #120f1a;
+ --term-chrome: #1b1626;
+ --term-line: rgba(255, 255, 255, 0.08);
+ --term-title: #8d84a6;
+ --term-ink: #ede9f7;
+ --term-out: #9d93b8;
+ --term-ok: #7ee2a8;
+ --term-prompt: #b394f2;
+ --shadow: 0 1px 2px rgba(20, 12, 40, 0.05), 0 12px 32px -12px rgba(20, 12, 40, 0.18);
+ --sans: "Geist", ui-sans-serif, system-ui, -apple-system, sans-serif;
+ --mono: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
+ --wrap: 72rem;
+ color-scheme: light;
}
@media (prefers-color-scheme: dark) {
:root {
- --bg: #16141c;
- --ink: #f0eef6;
- --muted: #a99ebd;
+ --bg: #0e0c13;
+ --bg-tint: #14111c;
+ --panel: #16121f;
+ --ink: #f2eff8;
+ --muted: #a9a0c2;
+ --faint: #928aab;
--accent: #b394f2;
- --accent-strong: #c9b2f7;
- --accent-soft: rgba(179, 148, 242, 0.12);
- --rule: #2c2836;
+ --accent-ink: #c9b2f7;
+ --accent-soft: rgba(179, 148, 242, 0.1);
+ --btn-bg: #6a48c8;
+ --btn-bg-hover: #7d5cdb;
+ --btn-ink: #ffffff;
+ --line: rgba(242, 239, 248, 0.09);
+ --line-strong: rgba(242, 239, 248, 0.16);
+ --band-bg: #1a1428;
+ --band-ink: #f2eef9;
+ --band-muted: #b3a8d2;
+ --band-line: rgba(242, 238, 249, 0.12);
+ --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 16px 40px -16px rgba(0, 0, 0, 0.55);
+ color-scheme: dark;
}
}
+
* { box-sizing: border-box; margin: 0; }
+html { scroll-behavior: smooth; }
body {
- background: var(--bg); color: var(--ink);
- font-family: var(--sans); line-height: 1.6;
- padding: 0 1.25rem;
-}
-main, footer { max-width: var(--max); margin: 0 auto; }
-header.hero { max-width: var(--max); margin: 0 auto; padding: 5rem 0 3rem; }
-.eyebrow { font-family: var(--mono); font-size: 0.8rem; color: var(--muted); letter-spacing: 0.08em; }
-.eyebrow a { color: var(--muted); text-decoration: none; }
-.eyebrow a:hover { color: var(--accent); }
-h1 { font-size: clamp(2rem, 7vw, 2.6rem); line-height: 1.15; margin: 0.5rem 0 1rem; }
-h1 .dot { color: var(--accent); }
-.lede { font-size: 1.15rem; color: var(--muted); max-width: 36rem; }
-.cta { margin: 2rem 0 0; display: flex; gap: 1rem; flex-wrap: wrap; align-items: center; }
-.cta a.primary {
- background: var(--accent); color: var(--bg); text-decoration: none;
- padding: 0.65rem 1.2rem; border-radius: 0.4rem; font-weight: 600;
-}
-.cta a.primary:hover { background: var(--accent-strong); }
-.cta a.secondary { color: var(--accent); text-decoration: none; padding: 0.65rem 0; font-weight: 600; }
-.cta a.secondary:hover, footer a:hover { text-decoration: underline; }
-a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 0.2em; }
+ background: var(--bg);
+ color: var(--ink);
+ font-family: var(--sans);
+ font-size: 1rem;
+ line-height: 1.6;
+ -webkit-font-smoothing: antialiased;
+ text-rendering: optimizeLegibility;
+}
::selection { background: var(--accent); color: var(--bg); }
-section { border-top: 1px solid var(--rule); padding: 2.5rem 0; }
-h2 { font-size: 1.3rem; margin-bottom: 1.25rem; }
-ol.ship { list-style: none; counter-reset: step; display: grid; gap: 1.25rem; }
-ol.ship li { counter-increment: step; padding-left: 2.4rem; position: relative; }
-ol.ship li::before {
- content: counter(step); position: absolute; left: 0; top: 0.1rem;
- width: 1.6rem; height: 1.6rem; border-radius: 50%;
- background: var(--accent-soft); color: var(--accent);
- font-family: var(--mono); font-size: 0.85rem;
+a:focus-visible, button:focus-visible {
+ outline: 2px solid var(--accent);
+ outline-offset: 3px;
+ border-radius: 0.25em;
+}
+html { scrollbar-color: var(--line-strong) transparent; }
+
+.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 1.5rem; }
+.wrap.narrow { max-width: 50rem; }
+
+.skip {
+ position: absolute; left: 1rem; top: -3rem;
+ background: var(--btn-bg); color: var(--btn-ink);
+ padding: 0.5rem 1rem; border-radius: 0.5rem; text-decoration: none;
+ transition: top 120ms ease-out; z-index: 20;
+}
+.skip:focus-visible { top: 1rem; }
+
+/* ---- Header ---- */
+.site-head {
+ position: sticky; top: 0; z-index: 10;
+ background: color-mix(in srgb, var(--bg) 82%, transparent);
+ backdrop-filter: blur(12px);
+ -webkit-backdrop-filter: blur(12px);
+ border-bottom: 1px solid transparent;
+ transition: border-color 160ms ease-out;
+}
+.site-head.scrolled { border-bottom-color: var(--line); }
+.site-head .wrap {
+ display: flex; align-items: center; gap: 2rem;
+ height: 4rem;
+}
+.brand {
+ font-weight: 640; font-size: 1.05rem; letter-spacing: -0.01em;
+ color: var(--ink); text-decoration: none;
+ display: flex; align-items: center; gap: 0.5rem;
+}
+.brand .mark { font-size: 1.1rem; }
+.site-head nav {
+ display: flex; gap: 1.75rem; margin-left: auto; align-items: center;
+}
+.site-head nav a {
+ color: var(--muted); text-decoration: none; font-size: 0.95rem; font-weight: 480;
+ transition: color 120ms ease-out;
+}
+.site-head nav a:hover { color: var(--ink); }
+.site-head .head-cta {
+ margin-left: 0.25rem;
+ color: var(--accent); font-weight: 560;
+ border: 1px solid var(--line-strong); border-radius: 0.55rem;
+ padding: 0.42rem 0.95rem;
+ transition: border-color 120ms ease-out, background 120ms ease-out, color 120ms ease-out;
+}
+.site-head .head-cta:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
+@media (max-width: 44rem) {
+ .site-head nav a:not(.head-cta) { display: none; }
+}
+
+/* ---- Hero ---- */
+.hero { padding: 5.5rem 0 5rem; }
+.nobr { white-space: nowrap; }
+.hero .wrap {
+ display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
+ gap: 4.5rem; align-items: center;
+}
+h1 {
+ font-size: clamp(2.6rem, 5.4vw, 4.1rem);
+ line-height: 1.04; letter-spacing: -0.032em; font-weight: 680;
+ text-wrap: balance;
+ margin-bottom: 1.35rem;
+}
+h1 .dot { color: var(--accent); }
+.lede {
+ font-size: 1.17rem; line-height: 1.65; color: var(--muted);
+ max-width: 34rem; text-wrap: pretty;
+}
+.cta { margin-top: 2.25rem; display: flex; gap: 1rem; flex-wrap: wrap; align-items: center; }
+.btn {
+ display: inline-flex; align-items: center; gap: 0.5rem;
+ text-decoration: none; font-weight: 580; font-size: 1rem;
+ border-radius: 0.65rem; padding: 0.72rem 1.4rem;
+ transition: background 130ms ease-out, border-color 130ms ease-out, transform 130ms ease-out;
+}
+.btn:active { transform: translateY(1px); }
+.btn-primary {
+ background: var(--btn-bg); color: var(--btn-ink);
+ box-shadow: 0 1px 2px rgba(20, 12, 40, 0.2), 0 6px 16px -6px color-mix(in srgb, var(--btn-bg) 55%, transparent);
+}
+.btn-primary:hover { background: var(--btn-bg-hover); }
+.btn-quiet {
+ color: var(--ink); border: 1px solid var(--line-strong);
+}
+.btn-quiet:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
+.hero-note {
+ margin-top: 1.4rem; font-size: 0.92rem; color: var(--faint);
+}
+.hero-note a { color: var(--faint); }
+.hero-note a:hover { color: var(--accent); }
+
+/* ---- Terminal ---- */
+.term {
+ background: var(--term-bg);
+ border: 1px solid var(--term-line);
+ border-radius: 0.85rem;
+ box-shadow: var(--shadow);
+ overflow: hidden;
+ font-family: var(--mono);
+ font-size: 0.86rem;
+ font-variant-numeric: tabular-nums;
+ line-height: 1.75;
+}
+.term-bar {
+ display: flex; align-items: center; gap: 0.45rem;
+ background: var(--term-chrome);
+ border-bottom: 1px solid var(--term-line);
+ padding: 0.6rem 0.9rem;
+}
+.term-bar i {
+ width: 0.68rem; height: 0.68rem; border-radius: 50%;
+ background: currentColor; opacity: 0.28; color: var(--term-title);
+}
+.term-bar span {
+ margin-left: 0.4rem; color: var(--term-title);
+ font-size: 0.78rem; letter-spacing: 0.01em;
+}
+.term-body { padding: 1.05rem 1.15rem 1.2rem; min-height: 17.5rem; }
+.term-body .line { display: block; white-space: pre-wrap; word-break: break-word; color: var(--term-out); }
+.term-body .cmd { color: var(--term-ink); }
+.term-body .cmd::before { content: "$ "; color: var(--term-prompt); font-weight: 560; }
+.term-body .cmt { color: var(--term-title); }
+.term-body .cmt::before { content: "$ "; color: var(--term-prompt); font-weight: 560; }
+.term-body .ok { color: var(--term-ok); }
+.term-caret {
+ display: inline-block; width: 0.55em; height: 1.05em;
+ background: var(--term-prompt); vertical-align: text-bottom;
+ animation: caret 1.1s steps(1) infinite;
+}
+@keyframes caret { 50% { opacity: 0; } }
+
+/* ---- Sections ---- */
+section { padding: 5.5rem 0; }
+section.tint { background: var(--bg-tint); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
+h2 {
+ font-size: clamp(1.7rem, 3vw, 2.3rem);
+ letter-spacing: -0.025em; line-height: 1.15; font-weight: 650;
+ text-wrap: balance;
+ margin-bottom: 0.9rem;
+}
+.section-lede { color: var(--muted); font-size: 1.08rem; max-width: 38rem; text-wrap: pretty; }
+
+.steps {
+ list-style: none; padding: 0;
+ margin-top: 3rem;
+ display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 2.75rem;
+}
+.steps li { position: relative; padding-top: 1.15rem; }
+.steps li::before {
+ content: counter(step); counter-increment: step;
+ position: absolute; top: -0.9rem; left: 0;
+ font-family: var(--mono); font-weight: 560; font-size: 0.82rem;
+ color: var(--accent);
+ background: var(--accent-soft);
+ width: 1.85rem; height: 1.85rem; border-radius: 50%;
+ display: grid; place-items: center;
+}
+.steps { counter-reset: step; }
+.steps h3 {
+ font-size: 1.12rem; font-weight: 620; letter-spacing: -0.01em;
+ margin: 0.9rem 0 0.45rem;
+}
+.steps p { color: var(--muted); font-size: 0.98rem; }
+.steps code {
+ font-family: var(--mono); font-size: 0.88em;
+ background: var(--accent-soft); color: var(--accent-ink);
+ padding: 0.12em 0.4em; border-radius: 0.3em;
+}
+
+.features {
+ list-style: none; padding: 0;
+ margin-top: 3rem;
+ display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 1.25rem;
+}
+.features li {
+ background: var(--panel);
+ border: 1px solid var(--line);
+ border-radius: 0.9rem;
+ padding: 1.6rem 1.5rem 1.7rem;
+ box-shadow: 0 1px 2px rgba(20, 12, 40, 0.04);
+ transition: border-color 140ms ease-out, transform 140ms ease-out;
+}
+.features li:hover { border-color: var(--line-strong); transform: translateY(-2px); }
+.features .icon {
+ width: 2.4rem; height: 2.4rem; border-radius: 0.6rem;
display: grid; place-items: center;
+ background: var(--accent-soft); color: var(--accent);
+ margin-bottom: 1.05rem;
}
-code { font-family: var(--mono); font-size: 0.9em; background: var(--accent-soft); padding: 0.1em 0.35em; border-radius: 0.25em; }
-pre {
- background: var(--accent-soft); padding: 1rem 1.25rem; border-radius: 0.5rem;
- overflow-x: auto; font-family: var(--mono); font-size: 0.9rem; line-height: 1.5;
- margin: 1rem 0;
-}
-pre code { background: none; padding: 0; font-size: inherit; }
-ul.features { list-style: none; display: grid; gap: 0.9rem; }
-ul.features li strong { display: block; }
-ul.features li span { color: var(--muted); }
-article p, article ul { margin-bottom: 1rem; max-width: var(--max); }
-article h2 { margin-top: 0; }
-article a { color: var(--accent); text-decoration: none; }
-article a:hover { text-decoration: underline; }
-.note { border-left: 1px solid var(--rule); padding-left: 1rem; color: var(--muted); font-size: 0.95rem; }
-footer { border-top: 1px solid var(--rule); padding: 2rem 0 3rem; font-size: 0.9rem; color: var(--muted); }
-footer p { margin-bottom: 0.6rem; }
+.features .icon svg { width: 1.25rem; height: 1.25rem; }
+.features h3 { font-size: 1.08rem; font-weight: 620; letter-spacing: -0.01em; margin-bottom: 0.4rem; }
+.features p { color: var(--muted); font-size: 0.97rem; }
+
+/* ---- Open-source band ---- */
+.band { background: var(--band-bg); color: var(--band-ink); padding: 6rem 0; }
+.band h2 { color: var(--band-ink); }
+.band p { color: var(--band-muted); font-size: 1.08rem; max-width: 40rem; text-wrap: pretty; }
+.band p + p { margin-top: 1rem; }
+.band .cta { margin-top: 2.2rem; }
+.band .btn-primary { background: var(--band-ink); color: var(--band-bg); box-shadow: none; }
+.band .btn-primary:hover { background: #ffffff; }
+.band .btn-quiet { color: var(--band-ink); border-color: var(--band-line); }
+.band .btn-quiet:hover { border-color: var(--band-ink); color: var(--band-ink); background: rgba(255, 255, 255, 0.06); }
+.band .proof {
+ margin-top: 2.4rem; padding-top: 1.6rem; border-top: 1px solid var(--band-line);
+ font-size: 0.95rem; color: var(--band-muted);
+}
+.band .proof a { color: var(--band-ink); }
+.band ::selection { background: var(--band-ink); color: var(--band-bg); }
+
+/* ---- Closing CTA ---- */
+.close-cta { padding: 7rem 0; text-align: center; }
+.close-cta h2 { font-size: clamp(2rem, 4vw, 3rem); }
+.close-cta p { color: var(--muted); font-size: 1.1rem; }
+.close-cta .cta { justify-content: center; }
+
+/* ---- Footer ---- */
+footer {
+ border-top: 1px solid var(--line);
+ padding: 3rem 0 3.5rem;
+ font-size: 0.92rem; color: var(--muted);
+}
+footer p { max-width: 44rem; }
+footer p + p { margin-top: 0.7rem; }
footer a { color: var(--accent); text-decoration: none; }
+footer a:hover { text-decoration: underline; text-underline-offset: 0.2em; }
+footer code { font-family: var(--mono); font-size: 0.9em; }
+
+/* ---- Get-started article ---- */
+.page-hero { padding: 4.5rem 0 1rem; }
+.page-hero .lede { margin-top: 0; }
+article.guide { padding: 1.5rem 0 4rem; }
+article.guide section { padding: 2.4rem 0; border-top: 1px solid var(--line); }
+article.guide section:first-child { border-top: 0; }
+article.guide h2 { font-size: 1.45rem; margin-bottom: 1rem; }
+article.guide p, article.guide ul { color: var(--muted); max-width: 42rem; margin-bottom: 1rem; }
+article.guide ul { padding-left: 1.2rem; }
+article.guide li + li { margin-top: 0.5rem; }
+article.guide a { color: var(--accent); text-decoration: none; }
+article.guide a:hover { text-decoration: underline; text-underline-offset: 0.2em; }
+article.guide pre {
+ background: var(--term-bg); color: var(--term-ink);
+ border: 1px solid var(--term-line);
+ padding: 1rem 1.2rem; border-radius: 0.7rem;
+ overflow-x: auto;
+ font-family: var(--mono); font-size: 0.88rem; line-height: 1.7;
+ margin: 1.1rem 0 1.3rem;
+}
+article.guide pre code { background: none; padding: 0; color: inherit; font-size: inherit; }
+article.guide code {
+ font-family: var(--mono); font-size: 0.88em;
+ background: var(--accent-soft); color: var(--accent-ink);
+ padding: 0.12em 0.4em; border-radius: 0.3em;
+}
+article.guide .note {
+ border-left: 1px solid var(--line-strong);
+ padding-left: 1.1rem; color: var(--faint); font-size: 0.95rem;
+}
+
+/* ---- Responsive ---- */
+@media (max-width: 62rem) {
+ .hero { padding: 3.5rem 0 4.5rem; }
+ .hero .wrap { grid-template-columns: 1fr; gap: 3rem; }
+ .lede { max-width: 38rem; }
+ .steps { grid-template-columns: 1fr; gap: 2.25rem; }
+ .features { grid-template-columns: 1fr; }
+ section, .band { padding: 4rem 0; }
+ .close-cta { padding: 5rem 0; }
+}
+@media (min-width: 44rem) and (max-width: 62rem) {
+ .features { grid-template-columns: repeat(3, minmax(0, 1fr)); }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ html { scroll-behavior: auto; }
+ *, *::before, *::after { animation: none !important; transition: none !important; }
+}
diff --git a/site.js b/site.js
new file mode 100644
index 0000000..b4694ea
--- /dev/null
+++ b/site.js
@@ -0,0 +1,56 @@
+// πŸ€– Carloku β€” hand-written, dependency-free. Two jobs only:
+// 1. the hero terminal types its session once (skipped under reduced motion),
+// 2. the sticky header grows its hairline once the page scrolls.
+(function () {
+ "use strict";
+
+ var head = document.querySelector(".site-head");
+ if (head) {
+ var onScroll = function () {
+ head.classList.toggle("scrolled", window.scrollY > 8);
+ };
+ window.addEventListener("scroll", onScroll, { passive: true });
+ onScroll();
+ }
+
+ var body = document.querySelector(".term-body");
+ if (!body) return;
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
+
+ var lines = Array.prototype.slice.call(body.querySelectorAll(".line"));
+ var caretLine = lines.pop(); // trailing caret stays put until the end
+ var texts = lines.map(function (el) { return el.textContent; });
+ lines.forEach(function (el) {
+ el.textContent = "";
+ el.style.display = "none";
+ });
+ if (caretLine) caretLine.style.display = "none";
+
+ var li = 0;
+ function nextLine() {
+ if (li >= lines.length) {
+ if (caretLine) caretLine.style.display = "";
+ return;
+ }
+ var el = lines[li];
+ var text = texts[li];
+ li++;
+ el.style.display = "";
+ if (el.hasAttribute("data-type")) {
+ var ci = 0;
+ (function typeChar() {
+ if (ci <= text.length) {
+ el.textContent = text.slice(0, ci);
+ ci++;
+ setTimeout(typeChar, 14);
+ } else {
+ setTimeout(nextLine, 160);
+ }
+ })();
+ } else {
+ el.textContent = text;
+ setTimeout(nextLine, el.classList.contains("ok") ? 260 : 120);
+ }
+ }
+ setTimeout(nextLine, 450);
+})();