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>
7 files changed,
+685
−116
fonts/Geist-Variable.woff2binaryfonts/GeistMono-Variable.woff2binaryfonts/LICENSE-OFL.txt+92 −0get-started/index.html+32 −12index.html+147 −42site.css+358 −62site.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 -> 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 -> 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); |
| +})(); |