diff --git a/README.md b/README.md
index ed2f52e..6b2d9c0 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,26 @@
# levkin.ca
-Five design concepts for the Levkin software development company homepage.
+Design concepts for the Levkin software development company homepage.
+
+### Brand directions
| Option | Path | Vibe |
|--------|------|------|
-| **Spec** | `/spec/` | RFC documentation, endpoints, required properties |
-| **Slab** | `/slab/` | Brutalist poster, massive typography |
-| **Relay** | `/relay/` | Telegraph, signal chain, decoded messages |
-| **Vault** | `/vault/` | Institutional trust, enterprise gravitas |
-| **Stack** | `/stack/` | Sticky scroll layers L0–L6, services as a card stack |
+| **Spec** | `/spec/` | RFC documentation, endpoints, iliadobkin.com |
+| **Slab** | `/slab/` | Brutalist poster |
+| **Relay** | `/relay/` | Telegraph, decoded messages |
+| **Vault** | `/vault/` | Institutional trust |
-Open `/` to compare all five.
+### Stack variants (L0–L6, scroll stops at interface)
+
+| Variant | Path | Metaphor |
+|---------|------|----------|
+| **Cards** | `/stack/` | Dark sticky cards with stack lips |
+| **Folder** | `/stack-folder/` | Manila folders, side tabs |
+| **Paper** | `/stack-paper/` | Cream document pile |
+| **Trace** | `/stack-trace/` | Call-stack / devtools frames |
+
+Open `/` to compare all.
## Develop
diff --git a/index.html b/index.html
index 6fffc91..5ed90a6 100644
--- a/index.html
+++ b/index.html
@@ -157,16 +157,43 @@
}
footer a { color: var(--accent); text-decoration: none; }
footer code { font-family: 'DM Mono', monospace; font-size: 0.75rem; }
+ .section-label {
+ font-family: 'DM Mono', monospace;
+ font-size: 0.68rem;
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+ color: var(--muted);
+ margin: 2rem 0 1rem;
+ }
+ .grid-stacks { margin-bottom: 0; }
+ .preview--folder {
+ background: #e8e2d4;
+ color: #2a2824;
+ font-family: 'DM Mono', monospace;
+ font-size: 0.65rem;
+ border-left: 12px solid #c9a86c;
+ }
+ .preview--paper {
+ background: #3d3832;
+ font-size: 2rem;
+ }
+ .preview--trace {
+ background: #0d0d0f;
+ color: #6b9b6b;
+ font-family: 'DM Mono', monospace;
+ font-size: 0.6rem;
+ }
levkin.ca · round 3
- Five directions.
- Enriched from auto, caseware, iliadobkin, and jobs — plus Stack: scroll the layers. Company-first throughout.
+ Eight directions.
+ Five brand concepts + four stack variants (L0–L6, stops on time). Spec updated with iliadobkin.com.
+
Brand
+
+
+ Stack variants (scroll test)
+
diff --git a/spec/index.html b/spec/index.html
index dae00e7..da3c1a1 100644
--- a/spec/index.html
+++ b/spec/index.html
@@ -41,6 +41,7 @@
Abstract
This document describes Levkin , a Canadian software development practice specializing in production systems, business automation, and enterprise tooling. Remote across North American and European time zones. Levkin ships software that must work when nobody is watching — with error handling, documentation, and tests as non-optional requirements.
+ Quality engineering and SDET work are documented at iliadobkin.com — an interactive portfolio (Playwright-style test runner UI, career timeline, trace-driven debugging showcase).
AVAILABLE Currently taking on new engagements.
@@ -104,7 +105,12 @@
/quality-engineering
iliadobkin.com
- Senior SDET services — test automation, CI/CD pipelines, trace-driven debugging. Interactive portfolio at iliadobkin.com.
+ Senior SDET services — test automation, CI/CD pipelines, trace-driven debugging, contract QA leadership.
+
+ Portfolio: iliadobkin.com — runnable career specs, trace viewer
+ Remote (ET) · Canadian · git.levkin.ca for source
+ Playwright / JS automation · enterprise CI (Jenkins, Azure DevOps, GitHub Actions)
+
diff --git a/stack-folder/folder.css b/stack-folder/folder.css
new file mode 100644
index 0000000..4174e2d
--- /dev/null
+++ b/stack-folder/folder.css
@@ -0,0 +1,68 @@
+:root {
+ --nav: 2.5rem; --stick: 1rem; --step: 1.1rem; --unit: 56vh;
+ --mono: 'IBM Plex Mono', monospace; --sans: 'Instrument Sans', system-ui, sans-serif;
+}
+* { box-sizing: border-box; margin: 0; padding: 0; }
+body { font-family: var(--sans); background: #2a2824; color: #1a1814; }
+.nav {
+ position: fixed; top: 0; left: 0; right: 0; z-index: 100;
+ display: flex; gap: 1rem; padding: 0.5rem 1rem; font-family: var(--mono); font-size: 0.62rem;
+ background: rgba(42,40,36,0.95); color: #c4b8a8;
+}
+.nav a { color: #8a8278; text-decoration: none; }
+.depth { margin-left: auto; color: #d4a574; font-weight: 600; }
+.mount { max-width: 520px; margin: 0 auto; padding-top: var(--nav); }
+.scroll-section { height: var(--unit); }
+.scroll-section--final { height: calc(var(--unit) * 0.5); min-height: 240px; }
+.stop { height: 1px; margin-bottom: 3rem; }
+.folder {
+ position: sticky;
+ margin-left: 2.5rem;
+ background: #e8e2d4;
+ border: 1px solid #c4b8a8;
+ border-radius: 0 6px 6px 6px;
+ box-shadow: 4px 4px 0 rgba(0,0,0,0.15), 0 12px 28px rgba(0,0,0,0.25);
+ min-height: 140px;
+}
+.tab {
+ position: absolute;
+ left: -2.5rem;
+ top: 0;
+ width: 2.35rem;
+ height: 100%;
+ min-height: 120px;
+ writing-mode: vertical-rl;
+ transform: rotate(180deg);
+ font-family: var(--mono);
+ font-size: 0.55rem;
+ letter-spacing: 0.06em;
+ padding: 0.5rem 0.25rem;
+ border-radius: 6px 0 0 6px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: 500;
+}
+.f0 { top: calc(var(--nav) + var(--stick)); z-index: 1; }
+.f0 .tab { background: #c9a86c; color: #2a2824; }
+.f1 { top: calc(var(--nav) + var(--stick) + var(--step)); z-index: 2; }
+.f1 .tab { background: #a8c4d4; }
+.f2 { top: calc(var(--nav) + var(--stick) + var(--step)*2); z-index: 3; }
+.f2 .tab { background: #b8d4a8; }
+.f3 { top: calc(var(--nav) + var(--stick) + var(--step)*3); z-index: 4; }
+.f3 .tab { background: #d4b8c4; }
+.f4 { top: calc(var(--nav) + var(--stick) + var(--step)*4); z-index: 5; }
+.f4 .tab { background: #d4c8a8; }
+.f5 { top: calc(var(--nav) + var(--stick) + var(--step)*5); z-index: 6; }
+.f5 .tab { background: #c4c4c4; }
+.f6 { top: calc(var(--nav) + var(--stick) + var(--step)*6); z-index: 7; }
+.f6 .tab { background: #2a4a6b; color: #e8e2d4; }
+.body { padding: 1.1rem 1.25rem; }
+.body h1, .body h2 { font-size: 1.2rem; margin-bottom: 0.35rem; }
+.body p { font-size: 0.88rem; color: #4a4844; }
+.body a { color: #2a4a6b; }
+.avail { font-family: var(--mono); font-size: 0.65rem; color: #3d6b3d; margin-top: 0.35rem; }
+.btn { font-family: var(--mono); font-size: 0.68rem; padding: 0.4rem 0.7rem; background: #2a4a6b; color: #fff; text-decoration: none; border-radius: 3px; margin-right: 0.35rem; }
+.btn.ghost { background: transparent; color: #2a4a6b; border: 1px solid #2a4a6b; }
+.foot { display: flex; justify-content: space-between; max-width: 520px; margin: 0 auto; padding: 0 1rem 2rem; font-family: var(--mono); font-size: 0.6rem; color: #6a6458; }
+.foot a { color: #6a6458; text-decoration: none; }
diff --git a/stack-folder/folder.js b/stack-folder/folder.js
new file mode 100644
index 0000000..171d96c
--- /dev/null
+++ b/stack-folder/folder.js
@@ -0,0 +1,10 @@
+const sections = document.querySelectorAll('.scroll-section');
+const depthEl = document.getElementById('depth');
+const mid = () => window.innerHeight * 0.42;
+function tick() {
+ let a = 0;
+ sections.forEach((s) => { const r = s.getBoundingClientRect(); if (r.top <= mid() && r.bottom > mid()) a = +s.dataset.layer; });
+ depthEl.textContent = `L${a}`;
+}
+window.addEventListener('scroll', tick, { passive: true });
+tick();
diff --git a/stack-folder/index.html b/stack-folder/index.html
new file mode 100644
index 0000000..51ba390
--- /dev/null
+++ b/stack-folder/index.html
@@ -0,0 +1,26 @@
+
+
+
+
+
+ Levkin — Stack Folder
+
+
+
+
+
+ ← options cards L0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/stack-paper/index.html b/stack-paper/index.html
new file mode 100644
index 0000000..56f22b2
--- /dev/null
+++ b/stack-paper/index.html
@@ -0,0 +1,26 @@
+
+
+
+
+
+ Levkin — Stack Paper
+
+
+
+
+
+ ← options cards L0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/stack-paper/paper.css b/stack-paper/paper.css
new file mode 100644
index 0000000..45a8dc3
--- /dev/null
+++ b/stack-paper/paper.css
@@ -0,0 +1,46 @@
+:root {
+ --nav: 2.5rem; --stick: 0.85rem; --step: 0.95rem; --unit: 54vh;
+ --desk: #3d3832; --paper: #f6f3eb; --ink: #1a1814;
+}
+* { box-sizing: border-box; margin: 0; padding: 0; }
+body { font-family: 'Literata', Georgia, serif; background: var(--desk); color: var(--ink); }
+.nav {
+ position: fixed; top: 0; left: 0; right: 0; z-index: 100;
+ display: flex; gap: 1rem; padding: 0.5rem 1rem;
+ font-family: 'IBM Plex Mono', monospace; font-size: 0.62rem;
+ background: rgba(61,56,50,0.92); color: #c4b8a8;
+}
+.nav a { color: #9a9288; text-decoration: none; }
+#depth { margin-left: auto; color: #8b7355; font-weight: 600; }
+.mount { max-width: 480px; margin: 0 auto; padding: var(--nav) 1.25rem 0; }
+.scroll-section { height: var(--unit); }
+.scroll-section--final { height: calc(var(--unit) * 0.48); min-height: 220px; }
+.stop { height: 1px; margin-bottom: 3rem; }
+.sheet {
+ position: sticky;
+ background: var(--paper);
+ border: 1px solid #d4cfc4;
+ padding: 0;
+ box-shadow: 2px 3px 0 #e0dcd2, 4px 6px 0 #d8d4c8, 0 16px 32px rgba(0,0,0,0.2);
+}
+.edge {
+ display: block;
+ height: 6px;
+ background: linear-gradient(180deg, #ebe6dc, #f6f3eb);
+ border-bottom: 1px solid #e0dcd2;
+}
+.inner { padding: 1.15rem 1.35rem 1.25rem; }
+.inner h1 { font-size: 1.85rem; font-weight: 600; }
+.inner h2 { font-size: 1.15rem; font-weight: 600; margin-bottom: 0.3rem; }
+.sub { font-family: 'IBM Plex Mono', monospace; font-size: 0.65rem; color: #6a6458; margin-bottom: 0.5rem; }
+.inner p { font-size: 0.9rem; color: #4a4844; line-height: 1.55; }
+.inner a { color: #2a4a6b; }
+.s0 { top: calc(var(--nav) + var(--stick)); z-index: 1; transform: rotate(-0.4deg); margin-left: 0; }
+.s1 { top: calc(var(--nav) + var(--stick) + var(--step)); z-index: 2; transform: rotate(0.3deg); margin-left: 6px; }
+.s2 { top: calc(var(--nav) + var(--stick) + var(--step)*2); z-index: 3; transform: rotate(-0.2deg); margin-left: 12px; }
+.s3 { top: calc(var(--nav) + var(--stick) + var(--step)*3); z-index: 4; transform: rotate(0.5deg); margin-left: 8px; }
+.s4 { top: calc(var(--nav) + var(--stick) + var(--step)*4); z-index: 5; transform: rotate(-0.3deg); margin-left: 14px; }
+.s5 { top: calc(var(--nav) + var(--stick) + var(--step)*5); z-index: 6; transform: rotate(0.2deg); margin-left: 10px; }
+.s6 { top: calc(var(--nav) + var(--stick) + var(--step)*6); z-index: 7; transform: rotate(0deg); margin-left: 4px; }
+.foot { text-align: center; padding: 0 1rem 2.5rem; font-family: 'IBM Plex Mono', monospace; font-size: 0.6rem; color: #7a7468; }
+.foot a { color: #7a7468; }
diff --git a/stack-paper/paper.js b/stack-paper/paper.js
new file mode 100644
index 0000000..171d96c
--- /dev/null
+++ b/stack-paper/paper.js
@@ -0,0 +1,10 @@
+const sections = document.querySelectorAll('.scroll-section');
+const depthEl = document.getElementById('depth');
+const mid = () => window.innerHeight * 0.42;
+function tick() {
+ let a = 0;
+ sections.forEach((s) => { const r = s.getBoundingClientRect(); if (r.top <= mid() && r.bottom > mid()) a = +s.dataset.layer; });
+ depthEl.textContent = `L${a}`;
+}
+window.addEventListener('scroll', tick, { passive: true });
+tick();
diff --git a/stack-trace/index.html b/stack-trace/index.html
new file mode 100644
index 0000000..033a44a
--- /dev/null
+++ b/stack-trace/index.html
@@ -0,0 +1,26 @@
+
+
+
+
+
+ Levkin — Stack Trace
+
+
+
+
+
+ ← options cards #0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/stack-trace/trace.css b/stack-trace/trace.css
new file mode 100644
index 0000000..8c6edd8
--- /dev/null
+++ b/stack-trace/trace.css
@@ -0,0 +1,48 @@
+:root {
+ --nav: 2.5rem; --stick: 0.9rem; --step: 1rem; --unit: 54vh;
+ --bg: #0d0d0f; --mono: 'IBM Plex Mono', monospace;
+}
+* { box-sizing: border-box; margin: 0; padding: 0; }
+body { font-family: var(--mono); background: var(--bg); color: #b8b4af; font-size: 0.8rem; }
+.nav {
+ position: fixed; top: 0; left: 0; right: 0; z-index: 100;
+ display: flex; gap: 1rem; padding: 0.5rem 1rem; font-size: 0.62rem;
+ background: rgba(13,13,15,0.95); border-bottom: 1px solid #2a2a30;
+}
+.nav a { color: #5a5854; text-decoration: none; }
+.nav a:hover { color: #7eb87a; }
+#depth { margin-left: auto; color: #7eb87a; }
+.mount { max-width: 560px; margin: 0 auto; padding-top: var(--nav); }
+.scroll-section { height: var(--unit); }
+.scroll-section--final { height: calc(var(--unit) * 0.5); min-height: 200px; }
+.stop { height: 1px; margin-bottom: 3rem; }
+.frame {
+ position: sticky;
+ margin: 0 1rem;
+ border-left: 3px solid #3a3a44;
+ background: #141418;
+ padding: 0.65rem 0 0.65rem 1rem;
+ box-shadow: 0 4px 0 #0a0a0c, 0 8px 20px rgba(0,0,0,0.35);
+}
+.frame code {
+ display: block;
+ font-size: 0.68rem;
+ color: #6b9b6b;
+ margin-bottom: 0.45rem;
+}
+.frame-body strong { color: #e8e6e3; font-weight: 500; }
+.frame-body p { color: #6b6966; font-size: 0.75rem; margin-top: 0.2rem; }
+.frame-body a { color: #8b9cb3; text-decoration: none; }
+.frame-body a:hover { color: #7eb87a; }
+.f0 { top: calc(var(--nav) + var(--stick)); z-index: 1; border-color: #c4a574; }
+.f1 { top: calc(var(--nav) + var(--stick) + var(--step)); z-index: 2; }
+.f2 { top: calc(var(--nav) + var(--stick) + var(--step)*2); z-index: 3; }
+.f3 { top: calc(var(--nav) + var(--stick) + var(--step)*3); z-index: 4; }
+.f4 { top: calc(var(--nav) + var(--stick) + var(--step)*4); z-index: 5; border-color: #6b8b9b; }
+.f5 { top: calc(var(--nav) + var(--stick) + var(--step)*5); z-index: 6; }
+.f6 { top: calc(var(--nav) + var(--stick) + var(--step)*6); z-index: 7; border-color: #7eb87a; }
+.foot {
+ display: flex; justify-content: space-between; max-width: 560px; margin: 0 auto;
+ padding: 0 1.5rem 2.5rem; font-size: 0.6rem; color: #3a3a40;
+}
+.foot a { color: #3a3a40; text-decoration: none; }
diff --git a/stack-trace/trace.js b/stack-trace/trace.js
new file mode 100644
index 0000000..de10304
--- /dev/null
+++ b/stack-trace/trace.js
@@ -0,0 +1,10 @@
+const sections = document.querySelectorAll('.scroll-section');
+const depthEl = document.getElementById('depth');
+const mid = () => window.innerHeight * 0.42;
+function tick() {
+ let a = 0;
+ sections.forEach((s) => { const r = s.getBoundingClientRect(); if (r.top <= mid() && r.bottom > mid()) a = +s.dataset.layer; });
+ depthEl.textContent = `#${a}`;
+}
+window.addEventListener('scroll', tick, { passive: true });
+tick();
diff --git a/stack/index.html b/stack/index.html
index e67726d..bc5abc1 100644
--- a/stack/index.html
+++ b/stack/index.html
@@ -14,97 +14,66 @@
← options
+ folder · paper · trace
L0
-
-
-
-
-
Levkin
-
Software · Canada · remote
-
Boutique engineering — production systems, automation, enterprise. Fixed scope, documented handoff.
-
- 15+ yrs
- 8h→2m
- 24/7
-
-
Taking new engagements
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
Custom software
-
Web apps, APIs, internal tools.
-
- TS · Python · .NET · PostgreSQL · React
-
-
-
-
-
-
-
-
Automation
-
n8n · Zapier · CI/CD · webhooks · LLMs
-
-
-
-
-
-
-
CaseWare & CaseView
-
15+ years · CaseWare Intl, MNP, JazzIt · C# · .NET · release pipelines
-
-
-
-
-
-
-
Quality & ops
-
SDET · test automation · CI/CD traces.
-
- iliadobkin.com
- ·
- jobs.levkin.ca
-
-
-
Retries · docs · tests first · smallest fit
-
-
-
-
-
-
-
- L0 L1 L2 L3 L4
-
+
+
+
+ L0 L1 L2 L3 L4 L5 L6
diff --git a/stack/stack.css b/stack/stack.css
index 40a768a..9e6ba58 100644
--- a/stack/stack.css
+++ b/stack/stack.css
@@ -2,310 +2,137 @@
--bg: #0e0e10;
--mono: 'IBM Plex Mono', ui-monospace, monospace;
--sans: 'Instrument Sans', system-ui, sans-serif;
- --sticky-top: 3rem;
- --layer-offset: 1.35rem;
- --layer-scroll: 38vh;
+ --nav-h: 2.5rem;
+ --stick: 1.15rem;
+ --step: 1.25rem;
+ --scroll-unit: 58vh;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
-html { scroll-behavior: smooth; }
-
body {
font-family: var(--sans);
background: var(--bg);
color: #e8e6e3;
line-height: 1.5;
- overflow-x: hidden;
}
.nav {
position: fixed;
- top: 0;
- left: 0;
- right: 0;
- z-index: 100;
+ top: 0; left: 0; right: 0;
+ z-index: 200;
display: flex;
- justify-content: space-between;
align-items: center;
- padding: 0.65rem 1.25rem;
+ gap: 1rem;
+ padding: 0.55rem 1rem;
font-family: var(--mono);
- font-size: 0.68rem;
- background: rgba(14, 14, 16, 0.9);
+ font-size: 0.62rem;
+ background: rgba(14, 14, 16, 0.92);
backdrop-filter: blur(8px);
- border-bottom: 1px solid rgba(255, 255, 255, 0.05);
-}
-
-.nav a {
- color: #6b6966;
- text-decoration: none;
+ border-bottom: 1px solid rgba(255,255,255,0.05);
}
+.nav a { color: #6b6966; text-decoration: none; }
.nav a:hover { color: #c4a574; }
+.variants { margin-left: auto; color: #4a4844; }
+.variants a { color: #5a5854; }
+.depth { color: #c4a574; font-weight: 600; }
-.depth {
- color: #c4a574;
- font-weight: 600;
- font-variant-numeric: tabular-nums;
+.stack-mount {
+ max-width: 500px;
+ margin: 0 auto;
+ padding: var(--nav-h) 1rem 0;
}
-.stack-viewport {
- padding: 0.5rem 1.25rem 2rem;
- max-width: 520px;
- margin: 0 auto;
+/* Each section = one scroll “tick”; sticky card stacks inside */
+.scroll-section {
+ height: var(--scroll-unit);
+ position: relative;
+}
+
+.scroll-section--final {
+ height: calc(var(--scroll-unit) * 0.55);
+ min-height: 280px;
+}
+
+.stack-stop {
+ height: 1px;
+ margin-bottom: 4rem;
}
.layer {
position: sticky;
- min-height: auto;
- padding-bottom: var(--layer-scroll);
- margin-bottom: -calc(var(--layer-scroll) - var(--layer-offset) * 2);
- border-radius: 8px;
- border: 1px solid rgba(255, 255, 255, 0.07);
- box-shadow:
- 0 12px 28px rgba(0, 0, 0, 0.35),
- 0 2px 6px rgba(0, 0, 0, 0.2);
+ top: calc(var(--nav-h) + var(--stick));
+ margin: 0 0.5rem;
+ border-radius: 6px 6px 4px 4px;
+ border: 1px solid rgba(255,255,255,0.08);
+ box-shadow: 0 8px 24px rgba(0,0,0,0.4);
}
-.layer-0 {
- top: var(--sticky-top);
- z-index: 1;
- background: #1a1a1e;
- margin-bottom: -calc(var(--layer-scroll) - var(--layer-offset));
-}
-
-.layer-1 {
- top: calc(var(--sticky-top) + var(--layer-offset));
- z-index: 2;
- background: #222228;
-}
-
-.layer-2 {
- top: calc(var(--sticky-top) + var(--layer-offset) * 2);
- z-index: 3;
- background: #2a2a32;
-}
-
-.layer-3 {
- top: calc(var(--sticky-top) + var(--layer-offset) * 3);
- z-index: 4;
- background: #32323c;
-}
-
-.layer-4 {
- top: calc(var(--sticky-top) + var(--layer-offset) * 4);
- z-index: 5;
- background: #3a3a46;
- margin-bottom: 0;
- padding-bottom: 1rem;
-}
-
-.layer-inner {
- padding: 1.25rem 1.35rem 1.35rem;
-}
-
-.layer-head {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- gap: 0.35rem 0.75rem;
- margin-bottom: 0.75rem;
- padding-bottom: 0.5rem;
- border-bottom: 1px solid rgba(255, 255, 255, 0.06);
- font-family: var(--mono);
- font-size: 0.62rem;
-}
-
-.layer-id {
- color: #c4a574;
- font-weight: 600;
- letter-spacing: 0.06em;
-}
-
-.layer-name {
- color: #6b6966;
- text-transform: uppercase;
- letter-spacing: 0.1em;
-}
-
-.layer-link {
- margin-left: auto;
- color: #8b9cb3;
- text-decoration: none;
- font-size: 0.6rem;
-}
-
-.layer-link:hover { color: #c4a574; }
-
-.layer h1 {
- font-size: clamp(2rem, 8vw, 2.65rem);
- font-weight: 600;
- letter-spacing: -0.04em;
- margin-bottom: 0.2rem;
- line-height: 1.1;
-}
-
-.layer h2 {
- font-size: 1.25rem;
- font-weight: 600;
- letter-spacing: -0.02em;
- margin-bottom: 0.35rem;
-}
-
-.tagline {
- font-family: var(--mono);
- font-size: 0.68rem;
- color: #6b6966;
- margin-bottom: 0.5rem;
-}
-
-.body {
- color: #a8a6a1;
- font-size: 0.9rem;
- margin-bottom: 0.65rem;
-}
-
-.chips {
- display: flex;
- flex-wrap: wrap;
- gap: 0.35rem;
- margin-bottom: 0.5rem;
-}
-
-.chips span {
- font-family: var(--mono);
- font-size: 0.58rem;
- padding: 0.2rem 0.45rem;
- background: rgba(196, 165, 116, 0.1);
- color: #c4a574;
- border: 1px solid rgba(196, 165, 116, 0.2);
- border-radius: 3px;
-}
-
-.avail {
- font-family: var(--mono);
- font-size: 0.65rem;
- color: #7eb87a;
-}
-
-.stack-list {
- list-style: none;
- font-family: var(--mono);
- font-size: 0.68rem;
- color: #6b6966;
-}
-
-.sat-links {
- font-family: var(--mono);
- font-size: 0.68rem;
- margin-bottom: 0.85rem;
-}
-
-.sat-links a {
- color: #8b9cb3;
- text-decoration: none;
-}
-
-.sat-links a:hover { color: #c4a574; }
-
-.sat-links .sep {
- color: #3a3a40;
- margin: 0 0.25rem;
-}
-
-.contact-row {
- display: flex;
- flex-wrap: wrap;
- gap: 0.5rem;
- margin-bottom: 0.65rem;
-}
-
-.btn {
- font-family: var(--mono);
- font-size: 0.72rem;
- padding: 0.5rem 0.85rem;
- background: #c4a574;
- color: #0e0e10;
- text-decoration: none;
- font-weight: 600;
- border-radius: 4px;
-}
-
-.btn:hover { background: #e8d4b0; }
-
-.btn-ghost {
- background: transparent;
- color: #c4a574;
- border: 1px solid rgba(196, 165, 116, 0.35);
-}
-
-.btn-ghost:hover {
- background: rgba(196, 165, 116, 0.08);
-}
-
-.guarantees {
- font-family: var(--mono);
- font-size: 0.6rem;
- color: #5a5854;
-}
-
-.site-foot {
- display: flex;
- justify-content: space-between;
- max-width: 520px;
- margin: 0 auto;
- padding: 1.5rem 1.25rem 3rem;
- font-family: var(--mono);
- font-size: 0.62rem;
- color: #4a4844;
-}
-
-.site-foot a {
- color: #4a4844;
- text-decoration: none;
-}
-
-.site-foot a:hover { color: #c4a574; }
-
-.stack-ruler {
- position: fixed;
- right: 1rem;
- top: 50%;
- transform: translateY(-50%);
- z-index: 50;
- display: flex;
- flex-direction: column;
- gap: 0.2rem;
- font-family: var(--mono);
- font-size: 0.5rem;
- color: #2e2e34;
- pointer-events: none;
-}
-
-.stack-ruler span {
- transition: color 0.15s;
-}
-
-.stack-ruler span.active {
- color: #c4a574;
-}
-
-.layer::before {
- content: '';
+/* Visible stack lip — previous layers peek through */
+.layer-tab {
position: absolute;
- top: 0;
- left: 0;
- right: 0;
- height: 2px;
- border-radius: 8px 8px 0 0;
- background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.08), transparent);
- pointer-events: none;
+ top: -5px;
+ left: 12px;
+ right: 12px;
+ height: 5px;
+ border-radius: 4px 4px 0 0;
+ background: inherit;
+ filter: brightness(1.15);
+ border: 1px solid rgba(255,255,255,0.06);
+ border-bottom: none;
+ box-shadow: 0 -2px 6px rgba(0,0,0,0.2);
}
-@media (max-width: 700px) {
- .stack-ruler { display: none; }
- :root {
- --layer-offset: 1rem;
- --layer-scroll: 32vh;
- }
+.layer-0 { z-index: 1; background: #1c1c20; top: calc(var(--nav-h) + var(--stick)); }
+.layer-1 { z-index: 2; background: #24242c; top: calc(var(--nav-h) + var(--stick) + var(--step)); }
+.layer-2 { z-index: 3; background: #2c2c36; top: calc(var(--nav-h) + var(--stick) + var(--step) * 2); }
+.layer-3 { z-index: 4; background: #343440; top: calc(var(--nav-h) + var(--stick) + var(--step) * 3); }
+.layer-4 { z-index: 5; background: #3c3c4a; top: calc(var(--nav-h) + var(--stick) + var(--step) * 4); }
+.layer-5 { z-index: 6; background: #444454; top: calc(var(--nav-h) + var(--stick) + var(--step) * 5); }
+.layer-6 { z-index: 7; background: #4c4c5e; top: calc(var(--nav-h) + var(--stick) + var(--step) * 6); }
+
+.layer-inner { padding: 1.1rem 1.25rem 1.2rem; }
+.layer-head {
+ display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem 0.65rem;
+ margin-bottom: 0.6rem; padding-bottom: 0.45rem;
+ border-bottom: 1px solid rgba(255,255,255,0.06);
+ font-family: var(--mono); font-size: 0.6rem;
+}
+.layer-id { color: #c4a574; font-weight: 600; }
+.layer-name { color: #6b6966; text-transform: uppercase; letter-spacing: 0.1em; }
+.layer-link { margin-left: auto; color: #8b9cb3; text-decoration: none; font-size: 0.58rem; }
+.layer-link:hover { color: #c4a574; }
+.layer h1 { font-size: 2rem; font-weight: 600; letter-spacing: -0.03em; line-height: 1.1; }
+.layer h2 { font-size: 1.15rem; font-weight: 600; margin-bottom: 0.3rem; }
+.tagline { font-family: var(--mono); font-size: 0.65rem; color: #6b6966; margin-bottom: 0.4rem; }
+.body { font-size: 0.88rem; color: #a8a6a1; margin-bottom: 0.5rem; }
+.chips { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-bottom: 0.4rem; }
+.chips span {
+ font-family: var(--mono); font-size: 0.55rem; padding: 0.15rem 0.4rem;
+ background: rgba(196,165,116,0.1); color: #c4a574;
+ border: 1px solid rgba(196,165,116,0.2); border-radius: 3px;
+}
+.avail { font-family: var(--mono); font-size: 0.62rem; color: #7eb87a; }
+.contact-row { display: flex; flex-wrap: wrap; gap: 0.45rem; margin: 0.65rem 0 0.4rem; }
+.btn {
+ font-family: var(--mono); font-size: 0.68rem; padding: 0.45rem 0.75rem;
+ background: #c4a574; color: #0e0e10; text-decoration: none; font-weight: 600; border-radius: 4px;
+}
+.btn-ghost { background: transparent; color: #c4a574; border: 1px solid rgba(196,165,116,0.35); }
+.guarantees { font-family: var(--mono); font-size: 0.58rem; color: #5a5854; }
+.site-foot {
+ display: flex; justify-content: space-between; max-width: 500px; margin: 0 auto;
+ padding: 0 1.5rem 2.5rem; font-family: var(--mono); font-size: 0.6rem; color: #4a4844;
+}
+.site-foot a { color: #4a4844; text-decoration: none; }
+.stack-ruler {
+ position: fixed; right: 0.75rem; top: 50%; transform: translateY(-50%);
+ z-index: 50; display: flex; flex-direction: column; gap: 0.15rem;
+ font-family: var(--mono); font-size: 0.48rem; color: #2a2a30; pointer-events: none;
+}
+.stack-ruler span.active { color: #c4a574; }
+@media (max-width: 700px) {
+ .stack-ruler, .variants { display: none; }
+ :root { --scroll-unit: 52vh; --step: 1rem; }
}
diff --git a/stack/stack.js b/stack/stack.js
index f1188ab..c77ab29 100644
--- a/stack/stack.js
+++ b/stack/stack.js
@@ -1,23 +1,16 @@
-const layers = document.querySelectorAll('.layer');
+const sections = document.querySelectorAll('.scroll-section');
const depthEl = document.getElementById('depth');
const rulerSpans = document.querySelectorAll('.stack-ruler span');
function updateDepth() {
- const mid = window.innerHeight * 0.45;
+ const mid = window.innerHeight * 0.42;
let active = 0;
-
- layers.forEach((layer) => {
- const rect = layer.getBoundingClientRect();
- if (rect.top <= mid && rect.bottom > mid) {
- active = Number(layer.dataset.layer);
- }
+ sections.forEach((sec) => {
+ const r = sec.getBoundingClientRect();
+ if (r.top <= mid && r.bottom > mid) active = Number(sec.dataset.layer);
});
-
depthEl.textContent = `L${active}`;
-
- rulerSpans.forEach((span, i) => {
- span.classList.toggle('active', i === active);
- });
+ rulerSpans.forEach((s, i) => s.classList.toggle('active', i === active));
}
window.addEventListener('scroll', updateDepth, { passive: true });
diff --git a/vite.config.js b/vite.config.js
index cb71672..eac37ee 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -11,6 +11,9 @@ export default defineConfig({
relay: resolve(__dirname, 'relay/index.html'),
vault: resolve(__dirname, 'vault/index.html'),
stack: resolve(__dirname, 'stack/index.html'),
+ stackFolder: resolve(__dirname, 'stack-folder/index.html'),
+ stackPaper: resolve(__dirname, 'stack-paper/index.html'),
+ stackTrace: resolve(__dirname, 'stack-trace/index.html'),
},
},
},