-
03 · Fixed
-
A new functional language
-
- Program against capabilities, not data structures.
-
-
-
-
- Fixed takes recent advances in programming language design —
- algebraic effects with evidence passing, Perceus reference counting,
- functional-but-in-place updates — and pushes the whole language up to
- a capabilities level. You describe what your methods need in order to function (cap);
- the compiler picks the datastructures and machine layout. Properties (prop)
- live alongside the capabilities they govern; refinement types are
- first-class.
-
-
- The design draws on
- Quine's predicate functor logic
- and Quantitative Type Theory. The language is still at Phase 0 — design — it is in flight,
- with eleven example programs exercising the language. The compiler
- (planned in Scala, targeting C) and the formal spec on their way.
+
+
+
+
+
+
+ 04 — Recent outcomes
+ Results you can put in a board deck.
+
+
+
+
+
+ 11h → 15m
+ pipeline runtime
+
+
+
Otravo · Travel e-commerce
+
A data pipeline measured in hours
+
A core pipeline ran for eleven hours — too slow to rerun,
+ too central to ignore. Rebuilt with optics and a narrowed
+ data model.
+
Eleven hours to fifteen minutes.
+
+
+
+
+
+ <20ms
+ P95 · 100% uptime · −30% infra
+
+
+
Bodireel · Consumer product
+
Fast, up, and cheaper to run
+
A consumer-facing system that had to stay fast and stay up
+ while the infrastructure bill came down — performance and
+ cost treated as design inputs.
+
P95 under 20ms, 100% uptime, 30% less infra.
+
+
+
+
+
+ |T|
+ total program cardinality
+
+
+
Royal Philips · Health technology
+
Correctness at programme scale
+
Static analysis grounded in Total Program Cardinality —
+ counting the inhabitants of a program's types to keep
+ quality measurable as the codebase grows.
+
A code-quality metric the compiler can enforce.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
agentsnon-deterministic, self-correcting
+
eval suitesbehaviour pinned down
+
static analysiswhole-program guarantees
+
propertieslaws, machine-checked
+
domain typesillegal states unrepresentable
+
+
+
+ - Scalable architecture Capability-based abstractions and algebraic effects that stay simple as the system grows.
+ - APIs & contracts Narrowed type signatures at every boundary — required means required.
+ - Security Illegal states unrepresentable beats illegal states validated.
+ - Performance P95s and pipeline runtimes treated as deliverables, not side effects.
+ - Clean code Fewer representable states means thousands of lines can go red — and be safely deleted.
+ - Maintainable systems Deterministic harnesses your team — and your agents — keep building against.
+
+
+
+
+
+
+
+
+
+
+
+ {% include libraries.html featured=true %}
+
+
+
+
Fixed — a new functional language
+
+ Program against capabilities, not data structures. Fixed
+ pushes algebraic effects, Perceus reference counting, and
+ functional-but-in-place updates up to a capabilities level:
+ you describe what your methods need (cap), the
+ compiler picks the data structures and machine layout.
+ Properties (prop) live alongside the capabilities
+ they govern; refinement types are first-class. Phase 0 —
+ design — with eleven example programs exercising the language.
-
+
- Read the design
-
+ class="btn btn-outline-primary" target="_blank" rel="noopener">Read the design
+ All libraries →
-
-
-
-
04 · Articles
-
Long-form on type systems, optics, cardinality, and LLM-augmented engineering.
- {% include medium-posts.html %}
-
-
- More on Medium →
-
-
-
+
+
+
+
+
+
+ 07 — Writing
+ Long-form on type systems, optics, cardinality, and LLM-augmented engineering.
+
+
+
+ {% include medium-posts.html %}
+
+
+
+ More on Medium →
+
+
+
+
+
+
+
+
+
+
diff --git a/_sass/home.scss b/_sass/home.scss
new file mode 100644
index 0000000..d8bc07a
--- /dev/null
+++ b/_sass/home.scss
@@ -0,0 +1,798 @@
+/* ============================================================
+ Landing page — "typed-systems editorial" layer.
+
+ Scoped to .cp-home (plus a body background hook). Tokens are CSS
+ custom properties so the existing light/dark theme toggle
+ (html[data-theme]) keeps working. Fraunces (variable serif) is the
+ display face for the landing page only; body copy stays on the
+ system sans stack defined in main.scss.
+
+ Motion notes: everything here is visible by default. GSAP
+ (assets/js/home.js) only *adds* entrances — no content is hidden
+ in CSS, so no-JS and prefers-reduced-motion users get the full
+ page, static.
+ ============================================================ */
+
+$hp-serif: "Fraunces", "Iowan Old Style", Georgia, "Times New Roman", serif;
+
+.cp-home {
+ /* --- Tokens: light (warm paper) ------------------------------- */
+ --hp-bg: #faf8f3;
+ --hp-ink: #1b1b26;
+ --hp-muted: rgba(27, 27, 38, 0.62);
+ --hp-faint: rgba(27, 27, 38, 0.45);
+ --hp-line: rgba(27, 27, 38, 0.14);
+ --hp-line-soft: rgba(27, 27, 38, 0.07);
+ --hp-accent: #3b3b8c;
+ --hp-accent-ink: #32327a;
+ --hp-accent-soft: rgba(59, 59, 140, 0.08);
+ --hp-accent-mid: rgba(59, 59, 140, 0.18);
+ --hp-card-bg: #fffdf9;
+ --hp-stat-bg: linear-gradient(135deg, rgba(59, 59, 140, 0.10), rgba(59, 59, 140, 0.03));
+
+ background: var(--hp-bg);
+ color: var(--hp-ink);
+ overflow-x: clip;
+ /* .site-header is a full-width left float the theme never clears.
+ Block children overlap it harmlessly, but flex sections (BFCs)
+ would be squeezed into the zero remaining space beside it. */
+ clear: both;
+}
+
+html[data-theme="dark"] .cp-home {
+ --hp-bg: #1f242c;
+ --hp-ink: #d9e0ea;
+ --hp-muted: rgba(217, 224, 234, 0.66);
+ --hp-faint: rgba(217, 224, 234, 0.45);
+ --hp-line: rgba(217, 224, 234, 0.16);
+ --hp-line-soft: rgba(217, 224, 234, 0.08);
+ --hp-accent: #9b9bf2;
+ --hp-accent-ink: #b3b3f7;
+ --hp-accent-soft: rgba(138, 138, 240, 0.10);
+ --hp-accent-mid: rgba(138, 138, 240, 0.22);
+ --hp-card-bg: #262c35;
+ --hp-stat-bg: linear-gradient(135deg, rgba(138, 138, 240, 0.14), rgba(138, 138, 240, 0.04));
+}
+
+/* Page background must extend behind navbar + footer, not just the
+ .cp-home block, or the warm paper ends in a hard seam. */
+html body:has(.cp-home) { background: #faf8f3; }
+html[data-theme="dark"] body:has(.cp-home) { background: #1f242c; }
+
+/* The theme paints header/footer with --background (pure white /
+ #22272e), which seams against the warm paper. Let them inherit. */
+body:has(.cp-home) .site-header,
+body:has(.cp-home) .site-footer { background: transparent; }
+
+/* --- Shared primitives ------------------------------------------ */
+
+.cp-home .hp-wrap {
+ max-width: 76rem;
+ margin-inline: auto;
+ padding-inline: clamp(1.25rem, 4vw, 3rem);
+}
+
+.cp-home .hp-section {
+ padding-block: clamp(4.5rem, 9vw, 8rem);
+ border-top: 1px solid var(--hp-line-soft);
+}
+
+.cp-home .hp-eyebrow {
+ font-family: $cp-mono;
+ font-size: 0.78rem;
+ font-weight: 600;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ color: var(--hp-accent);
+ margin: 0 0 1.25rem;
+}
+
+.cp-home .hp-h2 {
+ font-family: $hp-serif;
+ font-weight: 560;
+ font-variation-settings: "opsz" 90;
+ font-size: clamp(2.1rem, 4.5vw, 3.4rem);
+ line-height: 1.06;
+ letter-spacing: -0.015em;
+ margin: 0;
+ max-width: 22ch;
+ text-wrap: balance;
+}
+
+.cp-home .hp-h2 em {
+ font-style: italic;
+ color: var(--hp-accent-ink);
+}
+
+.cp-home .hp-section-head { margin-bottom: clamp(2.5rem, 5vw, 4.5rem); }
+
+.cp-home .hp-section-lead {
+ font-size: 1.15rem;
+ color: var(--hp-muted);
+ max-width: 52ch;
+ margin: 1.25rem 0 0;
+ line-height: 1.6;
+}
+
+.cp-home .hp-large {
+ font-size: clamp(1.2rem, 1.8vw, 1.45rem);
+ line-height: 1.55;
+ letter-spacing: -0.008em;
+ margin: 0;
+}
+
+.cp-home .hp-aside {
+ font-size: 0.92rem;
+ color: var(--hp-muted);
+ margin: 0.85rem 0 0;
+}
+
+.cp-home h3 {
+ font-family: $hp-serif;
+ font-weight: 600;
+ font-size: 1.45rem;
+ letter-spacing: -0.01em;
+ line-height: 1.15;
+ margin: 0 0 0.6rem;
+}
+
+.cp-home a:focus-visible,
+.cp-home .btn:focus-visible {
+ outline: 2px solid var(--hp-accent);
+ outline-offset: 3px;
+}
+
+/* Buttons inherit main.scss; re-derive colours from tokens so the
+ dark hero/dark section stay legible. */
+.cp-home .btn-primary {
+ background-color: var(--hp-accent);
+ border-color: var(--hp-accent);
+ color: var(--hp-bg);
+}
+.cp-home .btn-primary:hover { background-color: var(--hp-accent-ink); border-color: var(--hp-accent-ink); color: var(--hp-bg); }
+.cp-home .btn-outline-primary { color: var(--hp-accent); border-color: var(--hp-accent-mid); background: transparent; }
+.cp-home .btn-outline-primary:hover { background: var(--hp-accent-soft); color: var(--hp-accent-ink); border-color: var(--hp-accent); }
+
+/* The theme's html[data-theme="dark"] button rules (teal --link) outrank
+ the .cp-home overrides above; restate them at dark specificity. */
+html[data-theme="dark"] .cp-home .btn-primary {
+ background-color: var(--hp-accent);
+ border-color: var(--hp-accent);
+ color: #14141d;
+}
+html[data-theme="dark"] .cp-home .btn-primary:hover,
+html[data-theme="dark"] .cp-home .btn-primary:focus {
+ background-color: var(--hp-accent-ink);
+ border-color: var(--hp-accent-ink);
+ color: #14141d;
+}
+html[data-theme="dark"] .cp-home .btn-outline-primary {
+ color: var(--hp-accent);
+ border-color: var(--hp-accent-mid);
+}
+html[data-theme="dark"] .cp-home .btn-outline-primary:hover,
+html[data-theme="dark"] .cp-home .btn-outline-primary:focus {
+ background: var(--hp-accent-soft);
+ color: var(--hp-accent-ink);
+ border-color: var(--hp-accent);
+}
+
+/* ============================================================ Hero */
+
+.cp-home .hp-hero {
+ position: relative;
+ padding-block: clamp(4rem, 9vh, 7rem) clamp(4rem, 8vw, 6.5rem);
+ min-height: min(92vh, 60rem);
+ display: flex;
+ align-items: center;
+}
+
+.cp-home .hp-hero-bg {
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+ /* fine blueprint grid, faded at the edges */
+ background-image:
+ linear-gradient(var(--hp-line-soft) 1px, transparent 1px),
+ linear-gradient(90deg, var(--hp-line-soft) 1px, transparent 1px);
+ background-size: 72px 72px;
+ -webkit-mask-image: radial-gradient(120% 90% at 50% 10%, black 30%, transparent 75%);
+ mask-image: radial-gradient(120% 90% at 50% 10%, black 30%, transparent 75%);
+}
+
+.cp-home .hp-hero-bg canvas {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ opacity: 0.5;
+}
+
+.cp-home .hp-hero .hp-wrap { position: relative; width: 100%; }
+
+.cp-home .hp-hero-eyebrow { margin-bottom: 2rem; color: var(--hp-faint); }
+.cp-home .hp-hero-eyebrow span { color: var(--hp-accent); }
+
+.cp-home .hp-hero-title {
+ font-family: $hp-serif;
+ font-weight: 540;
+ font-variation-settings: "opsz" 144;
+ font-size: clamp(2.9rem, 8.2vw, 6.4rem);
+ line-height: 1.0;
+ letter-spacing: -0.022em;
+ margin: 0 0 1.75rem;
+ max-width: 14ch;
+}
+
+.cp-home .hp-hero-title em {
+ font-style: italic;
+ color: var(--hp-accent-ink);
+}
+
+.cp-home .hp-line { display: block; overflow: hidden; padding-block: 0.06em; }
+.cp-home .hp-line-inner { display: inline-block; will-change: transform; }
+
+.cp-home .hp-hero-lead {
+ font-size: clamp(1.1rem, 1.6vw, 1.3rem);
+ line-height: 1.6;
+ color: var(--hp-muted);
+ max-width: 46ch;
+ margin: 0 0 2.25rem;
+}
+
+.cp-home .hp-hero-cta {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.85rem;
+ align-items: center;
+}
+
+.cp-home .hp-magnetic { will-change: transform; }
+
+/* --- Narrowing diagram (HTML rebuild of hero-narrowing.svg) ------ */
+
+.cp-home .hp-narrowing {
+ margin: clamp(3rem, 6vw, 4.5rem) 0 0;
+ max-width: 64rem;
+}
+
+.cp-home .hp-bar-row { margin-bottom: 1.1rem; }
+.cp-home .hp-bar-row-2 { margin-left: 7%; }
+.cp-home .hp-bar-row-3 { margin-left: 14%; }
+
+.cp-home .hp-bar {
+ box-sizing: border-box;
+ display: flex;
+ align-items: center;
+ height: clamp(2.6rem, 5vw, 3.3rem);
+ padding-inline: clamp(0.9rem, 2vw, 1.4rem);
+ border: 1px solid var(--hp-accent);
+ border-radius: 0.55rem;
+ background: var(--hp-accent-soft);
+ transform-origin: left center;
+ overflow: hidden;
+ white-space: nowrap;
+}
+.cp-home .hp-bar-1 { width: 100%; }
+.cp-home .hp-bar-2 { width: 61.8%; background: var(--hp-accent-mid); }
+.cp-home .hp-bar-3 { width: 38.2%; background: var(--hp-accent-mid); border-width: 1.4px; }
+
+.cp-home .hp-bar code {
+ font-family: $cp-mono;
+ font-size: clamp(0.82rem, 1.5vw, 1.05rem);
+ font-weight: 500;
+ background: none;
+ color: var(--hp-ink);
+}
+.cp-home .hp-key { color: var(--hp-accent-ink); font-weight: 600; }
+.cp-home .hp-dim { color: var(--hp-faint); }
+
+.cp-home .hp-bar-sub {
+ font-size: 0.88rem;
+ color: var(--hp-muted);
+ margin: 0.45rem 0 0 0.15rem;
+}
+
+.cp-home .hp-bar-caption {
+ font-family: $cp-mono;
+ font-size: 0.78rem;
+ letter-spacing: 0.06em;
+ color: var(--hp-faint);
+ margin-top: 1.75rem;
+}
+
+@media (max-width: 640px) {
+ .cp-home .hp-bar-row-2 { margin-left: 3%; }
+ .cp-home .hp-bar-row-3 { margin-left: 6%; }
+ .cp-home .hp-bar-2 { width: 82%; }
+ .cp-home .hp-bar-3 { width: 70%; }
+ .cp-home .hp-bar-3 code { font-size: 0.7rem; }
+}
+
+/* ==================================================== Positioning */
+
+.cp-home .hp-positioning-grid {
+ display: grid;
+ gap: clamp(2.5rem, 5vw, 5rem);
+ align-items: start;
+}
+@media (min-width: 900px) {
+ .cp-home .hp-positioning-grid { grid-template-columns: 5fr 6fr; }
+}
+
+.cp-home .hp-capabilities {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 0;
+}
+@media (min-width: 560px) {
+ .cp-home .hp-capabilities { grid-template-columns: 1fr 1fr; column-gap: 2.5rem; }
+}
+
+.cp-home .hp-capabilities li {
+ border-top: 1px solid var(--hp-line);
+ padding: 1.1rem 0 1.3rem;
+ font-size: 0.97rem;
+ line-height: 1.5;
+ color: var(--hp-muted);
+}
+
+.cp-home .hp-capabilities strong {
+ display: block;
+ color: var(--hp-ink);
+ font-weight: 650;
+ letter-spacing: -0.01em;
+ margin-bottom: 0.3rem;
+ font-size: 1.05rem;
+}
+
+.cp-home .hp-cap-index {
+ float: right;
+ font-family: $cp-mono;
+ font-size: 0.72rem;
+ color: var(--hp-faint);
+ padding-top: 0.25rem;
+}
+
+/* ======================================================= Services */
+
+.cp-home .hp-services-intro {
+ display: grid;
+ gap: clamp(1.5rem, 3vw, 3rem);
+ margin-bottom: clamp(2.5rem, 5vw, 4rem);
+ max-width: 100%;
+}
+@media (min-width: 900px) {
+ .cp-home .hp-services-intro { grid-template-columns: 1fr 1fr; }
+}
+.cp-home .hp-services-intro p {
+ margin: 0;
+ font-size: 1.04rem;
+ line-height: 1.65;
+ color: var(--hp-muted);
+}
+.cp-home .hp-services-intro strong,
+.cp-home .hp-services-intro em { color: var(--hp-ink); }
+
+.cp-home .hp-services-grid {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 1.1rem;
+}
+@media (min-width: 760px) {
+ .cp-home .hp-services-grid { grid-template-columns: 1fr 1fr; }
+}
+
+.cp-home .hp-card {
+ display: flex;
+ flex-direction: column;
+ border: 1px solid var(--hp-line);
+ border-radius: 0.9rem;
+ background: var(--hp-card-bg);
+ padding: clamp(1.4rem, 2.5vw, 2rem);
+ transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1),
+ border-color 0.35s ease,
+ box-shadow 0.35s ease;
+}
+
+.cp-home .hp-card:hover {
+ transform: translateY(-5px);
+ border-color: var(--hp-accent-mid);
+ box-shadow: 0 18px 40px -22px rgba(27, 27, 60, 0.35);
+}
+
+.cp-home .hp-card-tag {
+ font-family: $cp-mono;
+ font-size: 0.7rem;
+ font-weight: 600;
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+ color: var(--hp-faint);
+ margin: 0 0 1.4rem;
+}
+
+.cp-home .hp-card-body {
+ font-size: 0.99rem;
+ line-height: 1.6;
+ color: var(--hp-muted);
+ margin: 0 0 1.4rem;
+}
+
+.cp-home .hp-card-outcome {
+ font-size: 1.02rem;
+ line-height: 1.5;
+ color: var(--hp-ink);
+ font-weight: 550;
+ letter-spacing: -0.006em;
+ border-top: 1px solid var(--hp-line-soft);
+ padding-top: 1.1rem;
+ margin: auto 0 1.4rem;
+}
+
+.cp-home .hp-card-link {
+ font-family: $cp-mono;
+ font-size: 0.78rem;
+ font-weight: 600;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ color: var(--hp-accent);
+ text-decoration: none;
+ align-self: flex-start;
+ position: relative;
+}
+.cp-home .hp-card-link::after {
+ content: "";
+ position: absolute;
+ left: 0; bottom: -3px;
+ width: 100%;
+ height: 1px;
+ background: var(--hp-accent);
+ transform: scaleX(0);
+ transform-origin: left center;
+ transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
+}
+.cp-home .hp-card-link:hover::after { transform: scaleX(1); }
+
+.cp-home .hp-services-cta {
+ margin-top: clamp(2.5rem, 5vw, 4rem);
+ text-align: center;
+}
+
+/* ======================================================== Process */
+
+.cp-home .hp-process {
+ position: relative;
+ max-width: 46rem;
+}
+
+.cp-home .hp-process-rail {
+ position: absolute;
+ top: 0.4rem;
+ bottom: 0.4rem;
+ left: 0.45rem;
+ width: 1px;
+ background: var(--hp-line);
+}
+.cp-home .hp-process-line {
+ position: absolute;
+ inset: 0;
+ background: var(--hp-accent);
+ transform-origin: top center;
+ /* JS scrubs scaleY 0→1; without JS the line is simply full. */
+}
+
+.cp-home .hp-steps {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+.cp-home .hp-step {
+ position: relative;
+ padding: 0 0 clamp(2.2rem, 4vw, 3.2rem) clamp(2.4rem, 5vw, 3.5rem);
+}
+.cp-home .hp-step:last-child { padding-bottom: 0; }
+
+.cp-home .hp-step::before {
+ content: "";
+ position: absolute;
+ left: 0.18rem;
+ top: 0.42rem;
+ width: 0.6rem;
+ height: 0.6rem;
+ border-radius: 50%;
+ background: var(--hp-bg);
+ border: 1.5px solid var(--hp-accent);
+}
+
+.cp-home .hp-step-num {
+ font-family: $cp-mono;
+ font-size: 0.75rem;
+ font-weight: 600;
+ letter-spacing: 0.16em;
+ color: var(--hp-accent);
+ margin: 0 0 0.35rem;
+}
+
+.cp-home .hp-step p:last-child {
+ margin: 0;
+ font-size: 0.99rem;
+ line-height: 1.6;
+ color: var(--hp-muted);
+ max-width: 52ch;
+}
+
+.cp-home .hp-pullquote {
+ margin: clamp(3rem, 6vw, 5rem) 0 0 auto;
+ max-width: 38rem;
+ border: 0;
+ border-left: 2px solid var(--hp-accent);
+ padding: 0.4rem 0 0.4rem 1.75rem;
+}
+.cp-home .hp-pullquote p {
+ font-family: $hp-serif;
+ font-style: italic;
+ font-size: clamp(1.25rem, 2.2vw, 1.6rem);
+ line-height: 1.4;
+ margin: 0 0 0.85rem;
+}
+.cp-home .hp-pullquote footer {
+ font-family: $cp-mono;
+ font-size: 0.78rem;
+ letter-spacing: 0.08em;
+ color: var(--hp-faint);
+}
+
+/* =================================================== Case studies */
+
+.cp-home .hp-cases {
+ display: flex;
+ flex-direction: column;
+ gap: clamp(2.5rem, 5vw, 4.5rem);
+}
+
+.cp-home .hp-case {
+ display: grid;
+ gap: clamp(1.5rem, 3vw, 3rem);
+ align-items: center;
+}
+@media (min-width: 860px) {
+ .cp-home .hp-case { grid-template-columns: 7fr 5fr; }
+ .cp-home .hp-case:nth-child(even) .hp-case-visual { order: 2; }
+}
+
+.cp-home .hp-case-visual {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 0.5rem;
+ aspect-ratio: 16 / 9;
+ border: 1px solid var(--hp-line);
+ border-radius: 1.1rem;
+ background:
+ linear-gradient(var(--hp-line-soft) 1px, transparent 1px) 0 0 / 48px 48px,
+ linear-gradient(90deg, var(--hp-line-soft) 1px, transparent 1px) 0 0 / 48px 48px,
+ var(--hp-stat-bg);
+ overflow: hidden;
+}
+
+.cp-home .hp-case-stat {
+ font-family: $hp-serif;
+ font-weight: 540;
+ font-variation-settings: "opsz" 144;
+ font-size: clamp(3rem, 7vw, 5.5rem);
+ letter-spacing: -0.03em;
+ color: var(--hp-accent-ink);
+ line-height: 1;
+}
+
+.cp-home .hp-case-statsub {
+ font-family: $cp-mono;
+ font-size: 0.78rem;
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+ color: var(--hp-muted);
+}
+
+.cp-home .hp-case-copy .hp-eyebrow { margin-bottom: 0.85rem; }
+.cp-home .hp-case-copy h3 { max-width: 18ch; }
+.cp-home .hp-case-copy p {
+ font-size: 0.99rem;
+ line-height: 1.6;
+ color: var(--hp-muted);
+ max-width: 44ch;
+ margin: 0 0 1rem;
+}
+
+.cp-home .hp-case-result {
+ font-weight: 650;
+ color: var(--hp-ink) !important;
+ letter-spacing: -0.008em;
+}
+
+/* =========================================== Technical excellence */
+
+.cp-home .hp-dark {
+ --hp-bg: #14141d;
+ --hp-ink: #e8e8f2;
+ --hp-muted: rgba(232, 232, 242, 0.64);
+ --hp-faint: rgba(232, 232, 242, 0.42);
+ --hp-line: rgba(232, 232, 242, 0.16);
+ --hp-line-soft: rgba(232, 232, 242, 0.08);
+ --hp-accent: #a3a3f5;
+ --hp-accent-ink: #bcbcf8;
+ --hp-accent-soft: rgba(163, 163, 245, 0.10);
+ --hp-accent-mid: rgba(163, 163, 245, 0.26);
+
+ background: #14141d;
+ color: var(--hp-ink);
+ border-top: 0;
+}
+
+.cp-home .hp-excellence {
+ display: grid;
+ gap: clamp(2.5rem, 5vw, 5rem);
+ align-items: center;
+}
+@media (min-width: 900px) {
+ .cp-home .hp-excellence { grid-template-columns: 6fr 5fr; }
+}
+
+.cp-home .hp-layers {
+ display: flex;
+ flex-direction: column;
+ gap: 0.7rem;
+}
+
+.cp-home .hp-layer {
+ display: flex;
+ align-items: baseline;
+ gap: 1rem;
+ border: 1px solid var(--hp-accent-mid);
+ border-radius: 0.6rem;
+ background: var(--hp-accent-soft);
+ padding: 0.85rem 1.25rem;
+ margin-inline: auto;
+ width: 100%;
+ white-space: nowrap;
+ overflow: hidden;
+}
+/* each layer narrows the one above it — the hero motif, restated */
+.cp-home .hp-layer:nth-child(2) { width: 86%; }
+.cp-home .hp-layer:nth-child(3) { width: 72%; }
+.cp-home .hp-layer:nth-child(4) { width: 58%; }
+.cp-home .hp-layer:nth-child(5) { width: 44%; background: var(--hp-accent-mid); }
+
+.cp-home .hp-layer code {
+ font-family: $cp-mono;
+ font-size: 0.92rem;
+ font-weight: 600;
+ color: var(--hp-accent-ink);
+ background: none;
+}
+.cp-home .hp-layer span {
+ font-size: 0.85rem;
+ color: var(--hp-muted);
+ text-overflow: ellipsis;
+ overflow: hidden;
+}
+
+@media (max-width: 560px) {
+ .cp-home .hp-layer span { display: none; }
+ .cp-home .hp-layer:nth-child(4) { width: 66%; }
+ .cp-home .hp-layer:nth-child(5) { width: 54%; }
+}
+
+.cp-home .hp-depth {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+.cp-home .hp-depth li {
+ border-top: 1px solid var(--hp-line-soft);
+ padding: 0.95rem 0;
+ font-size: 0.95rem;
+ line-height: 1.55;
+ color: var(--hp-muted);
+}
+.cp-home .hp-depth li:last-child { border-bottom: 1px solid var(--hp-line-soft); }
+.cp-home .hp-depth strong {
+ color: var(--hp-ink);
+ font-weight: 650;
+ margin-right: 0.5rem;
+}
+
+/* ===================================== Research & open source */
+
+.cp-home .hp-fixed {
+ margin-top: clamp(3rem, 6vw, 5rem);
+ border-top: 1px solid var(--hp-line);
+ padding-top: clamp(2rem, 4vw, 3rem);
+ max-width: 56rem;
+}
+.cp-home .hp-fixed .hp-large { color: var(--hp-muted); }
+.cp-home .hp-fixed .hp-large strong,
+.cp-home .hp-fixed .hp-large code { color: var(--hp-ink); }
+.cp-home .hp-fixed-links {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.75rem;
+ margin: 1.75rem 0 0;
+}
+
+/* Cards from the shared includes, re-skinned to the landing tokens. */
+.cp-home .card {
+ background: var(--hp-card-bg);
+ border: 1px solid var(--hp-line);
+ border-radius: 0.9rem;
+ transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1), border-color 0.35s ease;
+}
+.cp-home .card:hover { transform: translateY(-4px); border-color: var(--hp-accent-mid); }
+html[data-theme="dark"] .cp-home .card { background: var(--hp-card-bg); border-color: var(--hp-line); }
+
+.cp-home .hp-writing-more {
+ margin: 2rem 0 0;
+ font-family: $cp-mono;
+ font-size: 0.85rem;
+ letter-spacing: 0.06em;
+}
+
+/* ====================================================== Final CTA */
+
+.cp-home .hp-final {
+ text-align: center;
+ padding-block: clamp(6rem, 12vw, 10rem);
+}
+
+.cp-home .hp-final .hp-eyebrow { margin-bottom: 1.75rem; }
+
+.cp-home .hp-availability {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.55rem;
+ border: 1px solid var(--hp-line);
+ border-radius: 99px;
+ padding: 0.45rem 1rem;
+ color: var(--hp-muted);
+}
+.cp-home .hp-availability-dot {
+ width: 0.5rem;
+ height: 0.5rem;
+ border-radius: 50%;
+ background: #3e9c5f;
+ animation: hp-pulse 2.4s ease-in-out infinite;
+}
+@keyframes hp-pulse {
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(62, 156, 95, 0.45); }
+ 50% { box-shadow: 0 0 0 6px rgba(62, 156, 95, 0); }
+}
+
+.cp-home .hp-final-title {
+ margin-inline: auto;
+ max-width: 18ch;
+ font-size: clamp(2.4rem, 5.5vw, 4.2rem);
+}
+
+.cp-home .hp-final-lead {
+ font-size: 1.08rem;
+ line-height: 1.65;
+ color: var(--hp-muted);
+ max-width: 52ch;
+ margin: 1.75rem auto 2.5rem;
+}
+
+.cp-home .hp-final .hp-hero-cta { justify-content: center; }
+
+/* ====================================== Reduced motion / no-JS */
+
+@media (prefers-reduced-motion: reduce) {
+ .cp-home .hp-availability-dot { animation: none; }
+ .cp-home .hp-card,
+ .cp-home .card,
+ .cp-home .hp-card-link::after { transition: none; }
+}
diff --git a/assets/css/main.scss b/assets/css/main.scss
index f9e6903..e2a3c42 100644
--- a/assets/css/main.scss
+++ b/assets/css/main.scss
@@ -766,3 +766,10 @@ html[data-theme="dark"] .site-footer .footer-icons a:hover { color: $cp-accent-d
back to invisible on stripped Linux font sets. */
.fa-adjust::before { content: "\25D0"; margin-right: .25em; } /* ◐ left half black */
.fa-adjust.fa-rotate-180::before { content: "\25D1"; margin-right: .25em; } /* ◑ right half black */
+
+/* ============================================================
+ Landing page redesign — "typed-systems editorial" layer.
+ Lives in _sass/home.scss; scoped to .cp-home. Imported last so
+ its tokens win over the generic card/button rules above.
+ ============================================================ */
+@import 'home';
diff --git a/assets/js/home.js b/assets/js/home.js
new file mode 100644
index 0000000..1af06d2
--- /dev/null
+++ b/assets/js/home.js
@@ -0,0 +1,211 @@
+/* Landing page motion — GSAP + ScrollTrigger (loaded via CDN, defer).
+ *
+ * Design constraints:
+ * - Content is fully visible without JS; this file only ADDS motion.
+ * - prefers-reduced-motion: no entrances, no parallax, no canvas.
+ * - Canvas pauses when the hero is off-screen or the tab is hidden.
+ */
+(function () {
+ 'use strict';
+
+ var reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
+ var home = document.querySelector('.cp-home');
+ if (!home) return;
+
+ /* ---------------------------------------------------- Canvas */
+ // A sparse field of drifting nodes; near nodes get a connecting
+ // line. Reads as "flowing logic" without demanding attention.
+ function startFlowField() {
+ var canvas = document.getElementById('hp-flow');
+ if (!canvas) return;
+ var ctx = canvas.getContext('2d');
+ var dpr = Math.min(window.devicePixelRatio || 1, 2);
+ var w = 0, h = 0, nodes = [], running = false, raf = 0;
+
+ function accent() {
+ return document.documentElement.getAttribute('data-theme') === 'dark'
+ ? { r: 155, g: 155, b: 242 }
+ : { r: 59, g: 59, b: 140 };
+ }
+
+ function resize() {
+ var rect = canvas.parentElement.getBoundingClientRect();
+ w = rect.width; h = rect.height;
+ canvas.width = w * dpr; canvas.height = h * dpr;
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
+ var count = Math.min(70, Math.floor(w * h / 26000));
+ nodes = [];
+ for (var i = 0; i < count; i++) {
+ nodes.push({
+ x: Math.random() * w,
+ y: Math.random() * h,
+ vx: (Math.random() - 0.5) * 0.22,
+ vy: (Math.random() - 0.5) * 0.22
+ });
+ }
+ }
+
+ function tick() {
+ if (!running) return;
+ ctx.clearRect(0, 0, w, h);
+ var c = accent();
+ var i, j, a, b, dx, dy, d2;
+ for (i = 0; i < nodes.length; i++) {
+ a = nodes[i];
+ // gentle sine drift — "flow", not bounce
+ a.x += a.vx + Math.sin((a.y + performance.now() * 0.02) * 0.004) * 0.12;
+ a.y += a.vy;
+ if (a.x < -20) a.x = w + 20; else if (a.x > w + 20) a.x = -20;
+ if (a.y < -20) a.y = h + 20; else if (a.y > h + 20) a.y = -20;
+ ctx.beginPath();
+ ctx.arc(a.x, a.y, 1.4, 0, Math.PI * 2);
+ ctx.fillStyle = 'rgba(' + c.r + ',' + c.g + ',' + c.b + ',0.35)';
+ ctx.fill();
+ }
+ for (i = 0; i < nodes.length; i++) {
+ for (j = i + 1; j < nodes.length; j++) {
+ a = nodes[i]; b = nodes[j];
+ dx = a.x - b.x; dy = a.y - b.y; d2 = dx * dx + dy * dy;
+ if (d2 < 110 * 110) {
+ ctx.beginPath();
+ ctx.moveTo(a.x, a.y);
+ ctx.lineTo(b.x, b.y);
+ ctx.strokeStyle = 'rgba(' + c.r + ',' + c.g + ',' + c.b + ',' +
+ (0.12 * (1 - d2 / (110 * 110))).toFixed(3) + ')';
+ ctx.lineWidth = 1;
+ ctx.stroke();
+ }
+ }
+ }
+ raf = requestAnimationFrame(tick);
+ }
+
+ function setRunning(on) {
+ if (on === running) return;
+ running = on;
+ if (on) raf = requestAnimationFrame(tick);
+ else cancelAnimationFrame(raf);
+ }
+
+ resize();
+ window.addEventListener('resize', resize);
+ document.addEventListener('visibilitychange', function () {
+ setRunning(!document.hidden && visible);
+ });
+ var visible = true;
+ new IntersectionObserver(function (entries) {
+ visible = entries[0].isIntersecting;
+ setRunning(visible && !document.hidden);
+ }).observe(canvas.parentElement);
+ setRunning(true);
+ }
+
+ if (!reduced) startFlowField();
+
+ /* ------------------------------------------------ GSAP motion */
+ if (reduced || typeof gsap === 'undefined') return;
+ if (typeof ScrollTrigger !== 'undefined') gsap.registerPlugin(ScrollTrigger);
+
+ var EASE = 'power3.out';
+
+ // Hero entrance — one timeline, compiler-pass pacing.
+ var hero = gsap.timeline({ defaults: { ease: EASE } });
+ hero
+ .from('[data-hero="eyebrow"]', { y: 14, autoAlpha: 0, duration: 0.7 })
+ .from('[data-hero="line"]', {
+ yPercent: 115, duration: 1.0, stagger: 0.12
+ }, '-=0.45')
+ .from('[data-hero="fade"]', {
+ y: 26, autoAlpha: 0, duration: 0.9, stagger: 0.14
+ }, '-=0.55')
+ // the narrowing: each bar enters wide, then narrows to its type
+ .from('.hp-bar-row', { autoAlpha: 0, y: 14, duration: 0.5, stagger: 0.28 }, '-=0.6')
+ .from('.hp-bar-2', { width: '100%', duration: 0.9, ease: 'power4.inOut', clearProps: 'width' }, '-=0.7')
+ .from('.hp-bar-3', { width: '100%', duration: 0.9, ease: 'power4.inOut', clearProps: 'width' }, '-=0.55')
+ .from('.hp-bar-caption', { autoAlpha: 0, duration: 0.6 }, '-=0.3');
+
+ if (typeof ScrollTrigger === 'undefined') return;
+
+ // Generic reveals.
+ gsap.utils.toArray('[data-reveal]').forEach(function (el) {
+ gsap.from(el, {
+ y: 32, autoAlpha: 0, duration: 0.95, ease: EASE,
+ scrollTrigger: { trigger: el, start: 'top 84%', once: true }
+ });
+ });
+
+ // Grouped reveals — children stagger.
+ gsap.utils.toArray('[data-reveal-group]').forEach(function (group) {
+ gsap.from(group.children, {
+ y: 24, autoAlpha: 0, duration: 0.8, ease: EASE, stagger: 0.08,
+ scrollTrigger: { trigger: group, start: 'top 84%', once: true }
+ });
+ });
+
+ // Service cards.
+ gsap.utils.toArray('[data-stagger]').forEach(function (grid) {
+ gsap.from(grid.children, {
+ y: 44, autoAlpha: 0, duration: 0.9, ease: EASE, stagger: 0.12,
+ scrollTrigger: { trigger: grid, start: 'top 82%', once: true }
+ });
+ });
+
+ // Process — the rail draws as you scroll; steps pop in.
+ var rail = document.querySelector('.hp-process-line');
+ if (rail) {
+ gsap.fromTo(rail, { scaleY: 0 }, {
+ scaleY: 1, ease: 'none',
+ scrollTrigger: {
+ trigger: '.hp-process',
+ start: 'top 75%', end: 'bottom 55%', scrub: 0.6
+ }
+ });
+ }
+ gsap.utils.toArray('[data-step]').forEach(function (step) {
+ gsap.from(step, {
+ x: -28, autoAlpha: 0, duration: 0.8, ease: EASE,
+ scrollTrigger: { trigger: step, start: 'top 82%', once: true }
+ });
+ });
+
+ // Case studies — copy reveals; the stat block drifts (parallax).
+ gsap.utils.toArray('[data-case]').forEach(function (item) {
+ gsap.from(item.querySelector('.hp-case-copy'), {
+ y: 36, autoAlpha: 0, duration: 0.95, ease: EASE,
+ scrollTrigger: { trigger: item, start: 'top 80%', once: true }
+ });
+ gsap.from(item.querySelector('.hp-case-visual'), {
+ autoAlpha: 0, scale: 0.96, duration: 1.0, ease: EASE,
+ scrollTrigger: { trigger: item, start: 'top 80%', once: true }
+ });
+ gsap.fromTo(item.querySelector('.hp-case-stat'), { y: 18 }, {
+ y: -18, ease: 'none',
+ scrollTrigger: { trigger: item, start: 'top bottom', end: 'bottom top', scrub: 0.8 }
+ });
+ });
+
+ // Excellence layers — each layer draws in, narrowing as it goes.
+ var layers = gsap.utils.toArray('[data-layer]');
+ if (layers.length) {
+ gsap.from(layers, {
+ width: 0, autoAlpha: 0, duration: 0.85, ease: 'power4.inOut', stagger: 0.12,
+ scrollTrigger: { trigger: '.hp-layers', start: 'top 78%', once: true }
+ });
+ }
+
+ /* --------------------------------------------- Magnetic CTAs */
+ if (window.matchMedia('(pointer: fine)').matches) {
+ document.querySelectorAll('.hp-magnetic').forEach(function (btn) {
+ var setX = gsap.quickTo(btn, 'x', { duration: 0.35, ease: 'power3.out' });
+ var setY = gsap.quickTo(btn, 'y', { duration: 0.35, ease: 'power3.out' });
+ btn.addEventListener('pointermove', function (e) {
+ var r = btn.getBoundingClientRect();
+ setX((e.clientX - (r.left + r.width / 2)) * 0.22);
+ setY((e.clientY - (r.top + r.height / 2)) * 0.3);
+ });
+ btn.addEventListener('pointerleave', function () {
+ gsap.to(btn, { x: 0, y: 0, duration: 0.6, ease: 'elastic.out(1, 0.45)' });
+ });
+ });
+ }
+})();
diff --git a/index.html b/index.html
index 1349d33..e4d427d 100644
--- a/index.html
+++ b/index.html
@@ -1,4 +1,3 @@
---
layout: home
-bootstrap: true
---