Rebuild stack-folder with sticky tab rail and site previews.
L0–L7 folders stack on scroll with aligned max depth, labeled tabs that stay consistent when L7 joins the rail, Cal embeds, preview screenshots, Playwright tests, and updated README. Co-authored-by: Cursor <cursoragent@cursor.com>
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* Cal.com inline embed — paste from Event type → consult → Embed → Inline.
|
||||
* Uses embed.js (not a raw iframe). No “allowed domains” setting needed for this path.
|
||||
* L0 = dark snippet, L7 = light snippet. Screenshot fallback if embed does not mount.
|
||||
*/
|
||||
const CAL_ORIGIN = 'https://cal.levkin.ca';
|
||||
const CAL_LINK = 'ilia/consult';
|
||||
const EMBED_SCRIPT = `${CAL_ORIGIN}/embed/embed.js`;
|
||||
|
||||
const SLOTS = {
|
||||
dark: {
|
||||
ns: 'consult-l0',
|
||||
inlineConfig: {
|
||||
layout: 'week_view',
|
||||
useSlotsViewOnSmallScreen: 'true',
|
||||
theme: 'dark',
|
||||
},
|
||||
ui: { theme: 'dark', hideEventTypeDetails: true, layout: 'week_view' },
|
||||
},
|
||||
light: {
|
||||
ns: 'consult-l7',
|
||||
inlineConfig: {
|
||||
layout: 'week_view',
|
||||
useSlotsViewOnSmallScreen: 'true',
|
||||
theme: 'light',
|
||||
},
|
||||
ui: { theme: 'light', hideEventTypeDetails: true, layout: 'week_view' },
|
||||
},
|
||||
};
|
||||
|
||||
let calApiReady;
|
||||
|
||||
/** Loader from Cal embed UI */
|
||||
function bootCalLoader() {
|
||||
if (window.Cal?.loaded) return;
|
||||
(function (C, A, L) {
|
||||
const p = (a, ar) => {
|
||||
a.q.push(ar);
|
||||
};
|
||||
const d = C.document;
|
||||
C.Cal =
|
||||
C.Cal ||
|
||||
function () {
|
||||
const cal = C.Cal;
|
||||
const ar = arguments;
|
||||
if (!cal.loaded) {
|
||||
cal.ns = {};
|
||||
cal.q = cal.q || [];
|
||||
d.head.appendChild(d.createElement('script')).src = A;
|
||||
cal.loaded = true;
|
||||
}
|
||||
if (ar[0] === L) {
|
||||
const api = function () {
|
||||
p(api, arguments);
|
||||
};
|
||||
const namespace = ar[1];
|
||||
api.q = api.q || [];
|
||||
if (typeof namespace === 'string') {
|
||||
cal.ns[namespace] = cal.ns[namespace] || api;
|
||||
p(cal.ns[namespace], ar);
|
||||
p(cal, ['initNamespace', namespace]);
|
||||
} else p(cal, ar);
|
||||
return;
|
||||
}
|
||||
p(cal, ar);
|
||||
};
|
||||
})(window, EMBED_SCRIPT, 'init');
|
||||
}
|
||||
|
||||
function waitForEmbedScript() {
|
||||
return new Promise((resolve) => {
|
||||
const finish = () => resolve(window.Cal);
|
||||
const attach = (s) => {
|
||||
if (s.dataset.calReady) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
s.addEventListener(
|
||||
'load',
|
||||
() => {
|
||||
s.dataset.calReady = '1';
|
||||
finish();
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
s.addEventListener('error', () => resolve(null), { once: true });
|
||||
};
|
||||
|
||||
const existing = document.querySelector(`script[src="${EMBED_SCRIPT}"]`);
|
||||
if (existing) {
|
||||
attach(existing);
|
||||
return;
|
||||
}
|
||||
|
||||
const mo = new MutationObserver(() => {
|
||||
const s = document.querySelector(`script[src="${EMBED_SCRIPT}"]`);
|
||||
if (!s) return;
|
||||
mo.disconnect();
|
||||
attach(s);
|
||||
});
|
||||
mo.observe(document.head, { childList: true });
|
||||
window.setTimeout(() => {
|
||||
mo.disconnect();
|
||||
finish();
|
||||
}, 12000);
|
||||
});
|
||||
}
|
||||
|
||||
function loadCalApi() {
|
||||
if (!calApiReady) {
|
||||
bootCalLoader();
|
||||
/* First init pulls in embed.js (same as Cal’s generated snippet) */
|
||||
window.Cal('init', 'consult', { origin: CAL_ORIGIN });
|
||||
calApiReady = waitForEmbedScript();
|
||||
}
|
||||
return calApiReady;
|
||||
}
|
||||
|
||||
function mountInline(slot) {
|
||||
const targetId = slot.dataset.calTarget;
|
||||
const themeKey = slot.dataset.calTheme === 'light' ? 'light' : 'dark';
|
||||
const spec = SLOTS[themeKey];
|
||||
const el = document.getElementById(targetId);
|
||||
if (!el || !spec || !window.Cal?.ns) return;
|
||||
|
||||
window.Cal('init', spec.ns, { origin: CAL_ORIGIN });
|
||||
|
||||
window.Cal.ns[spec.ns]('inline', {
|
||||
elementOrSelector: `#${targetId}`,
|
||||
config: spec.inlineConfig,
|
||||
calLink: CAL_LINK,
|
||||
});
|
||||
|
||||
window.Cal.ns[spec.ns]('ui', spec.ui);
|
||||
}
|
||||
|
||||
function watchSlot(slot) {
|
||||
const targetId = slot.dataset.calTarget;
|
||||
const el = document.getElementById(targetId);
|
||||
if (!el) return;
|
||||
|
||||
const showFallback = () => {
|
||||
slot.classList.add('is-blocked');
|
||||
slot.classList.remove('is-embedded');
|
||||
};
|
||||
const showEmbed = () => {
|
||||
slot.classList.remove('is-blocked');
|
||||
slot.classList.add('is-embedded');
|
||||
};
|
||||
|
||||
showFallback();
|
||||
|
||||
const hasLiveEmbed = () => {
|
||||
const iframe = el.querySelector('iframe');
|
||||
return Boolean(iframe && iframe.offsetHeight > 60);
|
||||
};
|
||||
|
||||
const obs = new MutationObserver(() => {
|
||||
if (hasLiveEmbed()) showEmbed();
|
||||
});
|
||||
obs.observe(el, { childList: true, subtree: true });
|
||||
|
||||
window.setTimeout(() => {
|
||||
obs.disconnect();
|
||||
if (!hasLiveEmbed()) showFallback();
|
||||
}, 8000);
|
||||
}
|
||||
|
||||
export function initCalEmbeds() {
|
||||
const slots = [...document.querySelectorAll('[data-cal-embed]')];
|
||||
if (!slots.length) return;
|
||||
|
||||
loadCalApi()
|
||||
.then((Cal) => {
|
||||
if (!Cal) return;
|
||||
slots.forEach((slot) => {
|
||||
mountInline(slot);
|
||||
watchSlot(slot);
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
/* screenshots + ↗ */
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/** Fixed rail — all L0–L7 visible; click to jump (syncs with scroll) */
|
||||
export function initRailRoller(rail = document.querySelector('.tab-rail')) {
|
||||
const tabs = rail ? [...rail.querySelectorAll('.rail-tab')] : [];
|
||||
if (!rail || !tabs.length) return;
|
||||
|
||||
function setActive(layer) {
|
||||
const n = Math.max(0, Math.min(tabs.length - 1, layer));
|
||||
tabs.forEach((btn, i) => btn.classList.toggle('active', i === n));
|
||||
}
|
||||
|
||||
function gotoLayer(layer) {
|
||||
setActive(layer);
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('stack-goto-layer', { detail: { layer } }),
|
||||
);
|
||||
}
|
||||
|
||||
tabs.forEach((btn) => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
gotoLayer(Number(btn.dataset.layer ?? btn.dataset.goto));
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener('stack-depth', (e) => {
|
||||
const layer = Number(e.detail?.active);
|
||||
if (Number.isFinite(layer)) setActive(layer);
|
||||
});
|
||||
|
||||
setActive(0);
|
||||
}
|
||||
@@ -2,142 +2,687 @@
|
||||
@import '../shared/stack-layout.css';
|
||||
|
||||
:root {
|
||||
--tab-offset: 2.35rem;
|
||||
--tab-offset: 2.1rem;
|
||||
--mono: 'IBM Plex Mono', monospace;
|
||||
--sans: 'Instrument Sans', system-ui, sans-serif;
|
||||
--text: 'Instrument Sans', system-ui, sans-serif;
|
||||
--display: 'Literata', Georgia, serif;
|
||||
--hi: #fff59d;
|
||||
--hi-edge: #f5e06a;
|
||||
--desk: #3d3830;
|
||||
--manila: #ebe4d4;
|
||||
--manila-edge: #c9bea8;
|
||||
--manila-dark: #d8cfbc;
|
||||
--ink: #1c1a16;
|
||||
--ink-muted: #4a4640;
|
||||
--l0-tab: #c9a86c;
|
||||
--l0-tab-fg: #2a2824;
|
||||
--l1-tab: #a8c4d4;
|
||||
--l1-tab-fg: #1a2830;
|
||||
--l2-tab: #b8d4a8;
|
||||
--l2-tab-fg: #1a2818;
|
||||
--l3-tab: #d4b8c4;
|
||||
--l3-tab-fg: #2a1820;
|
||||
--l4-tab: #d4c8a8;
|
||||
--l4-tab-fg: #2a2418;
|
||||
--l5-tab: #c4c4c4;
|
||||
--l5-tab-fg: #2a2a2a;
|
||||
--l6-tab: #6a7a8a;
|
||||
--l6-tab-fg: #f0ece4;
|
||||
--l7-tab: #2a4a6b;
|
||||
--l7-tab-fg: #ebe4d4;
|
||||
}
|
||||
|
||||
* { 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: 400;
|
||||
display: flex; flex-wrap: wrap; gap: 0.5rem 1rem; padding: 0.5rem 1rem;
|
||||
font-family: var(--mono); font-size: 0.62rem;
|
||||
background: rgba(42,40,36,0.97); color: #c4b8a8;
|
||||
body {
|
||||
font-family: var(--sans);
|
||||
background: var(--desk);
|
||||
color: var(--ink);
|
||||
background-image:
|
||||
linear-gradient(90deg, rgba(0,0,0,0.04) 1px, transparent 1px),
|
||||
linear-gradient(rgba(0,0,0,0.03) 1px, transparent 1px);
|
||||
background-size: 24px 24px;
|
||||
}
|
||||
|
||||
.nav a { color: #8a8278; text-decoration: none; }
|
||||
.nav a:hover { color: #d4a574; }
|
||||
.depth { margin-left: auto; color: #d4a574; font-weight: 600; }
|
||||
.site-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 400;
|
||||
padding: 0.5rem 1rem;
|
||||
font-family: var(--mono);
|
||||
font-size: 0.62rem;
|
||||
letter-spacing: 0.02em;
|
||||
text-align: center;
|
||||
color: #b8ad9d;
|
||||
background: rgba(42, 40, 36, 0.97);
|
||||
border-bottom: 1px solid #59534a;
|
||||
}
|
||||
|
||||
.site-header a {
|
||||
color: #d4a574;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.site-header a:hover {
|
||||
color: #ebc39a;
|
||||
}
|
||||
|
||||
.tab-rail {
|
||||
position: fixed;
|
||||
top: 2.75rem;
|
||||
right: max(0.5rem, calc(50% - 340px));
|
||||
top: calc(var(--stack-nav) + 0.35rem);
|
||||
left: calc(50% + 20rem + 1.35rem);
|
||||
right: auto;
|
||||
z-index: 500;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.2rem;
|
||||
padding: 0.35rem;
|
||||
background: rgba(42,40,36,0.92);
|
||||
gap: 0.15rem;
|
||||
padding: 0.3rem;
|
||||
background: rgba(42,40,36,0.94);
|
||||
border: 1px solid #4a4844;
|
||||
border-radius: 6px;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.rail-tab {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.55rem;
|
||||
padding: 0.2rem 0.45rem;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
padding: 0.22rem 0.5rem;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
color: #8a8278;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
min-width: 2.1rem;
|
||||
}
|
||||
|
||||
.rail-tab:hover,
|
||||
.rail-tab.active { background: #c9a86c; color: #2a2824; }
|
||||
.rail-tab[data-layer="0"]:hover,
|
||||
.rail-tab[data-layer="0"].active { background: var(--l0-tab); color: var(--l0-tab-fg); border-color: rgba(0,0,0,0.12); }
|
||||
.rail-tab[data-layer="1"]:hover,
|
||||
.rail-tab[data-layer="1"].active { background: var(--l1-tab); color: var(--l1-tab-fg); border-color: rgba(0,0,0,0.12); }
|
||||
.rail-tab[data-layer="2"]:hover,
|
||||
.rail-tab[data-layer="2"].active { background: var(--l2-tab); color: var(--l2-tab-fg); border-color: rgba(0,0,0,0.12); }
|
||||
.rail-tab[data-layer="3"]:hover,
|
||||
.rail-tab[data-layer="3"].active { background: var(--l3-tab); color: var(--l3-tab-fg); border-color: rgba(0,0,0,0.12); }
|
||||
.rail-tab[data-layer="4"]:hover,
|
||||
.rail-tab[data-layer="4"].active { background: var(--l4-tab); color: var(--l4-tab-fg); border-color: rgba(0,0,0,0.12); }
|
||||
.rail-tab[data-layer="5"]:hover,
|
||||
.rail-tab[data-layer="5"].active { background: var(--l5-tab); color: var(--l5-tab-fg); border-color: rgba(0,0,0,0.12); }
|
||||
.rail-tab[data-layer="6"]:hover,
|
||||
.rail-tab[data-layer="6"].active { background: var(--l6-tab); color: var(--l6-tab-fg); border-color: rgba(0,0,0,0.12); }
|
||||
.rail-tab[data-layer="7"]:hover,
|
||||
.rail-tab[data-layer="7"].active { background: var(--l7-tab); color: var(--l7-tab-fg); border-color: rgba(0,0,0,0.08); }
|
||||
|
||||
.mount {
|
||||
--folder-body-h: 30rem;
|
||||
width: min(640px, 100%);
|
||||
margin: 0 auto;
|
||||
padding: var(--stack-nav) 1rem 0;
|
||||
padding: calc(var(--stack-nav) + 0.75rem) 1rem 2rem;
|
||||
}
|
||||
|
||||
/* Scroll step between layers (folder uses display:contents) */
|
||||
.folder:not(.folder--last) .body {
|
||||
margin-bottom: calc(var(--stack-scroll-slot) + 5.5rem);
|
||||
}
|
||||
|
||||
.folder--last .body {
|
||||
margin-bottom: var(--stack-runway, 0px);
|
||||
}
|
||||
|
||||
/* Same pattern as /stack/ — one sticky card, next covers previous */
|
||||
.folder {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.folder .body {
|
||||
position: sticky;
|
||||
min-height: var(--stack-card-min);
|
||||
margin-bottom: 1.25rem;
|
||||
width: 100%;
|
||||
top: calc(var(--stack-stick) + var(--stack-tab-h) - 1px);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tab {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Each tab sticks above its folder body as you scroll the stack */
|
||||
.tab {
|
||||
position: sticky;
|
||||
display: block;
|
||||
width: fit-content;
|
||||
max-width: calc(100% - 1rem);
|
||||
top: var(--stack-stick);
|
||||
height: var(--stack-tab-h);
|
||||
box-sizing: border-box;
|
||||
width: max-content;
|
||||
max-width: none;
|
||||
white-space: nowrap;
|
||||
font-family: var(--mono);
|
||||
font-size: 0.62rem;
|
||||
font-size: 0.58rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
padding: 0.4rem 1rem 0.35rem;
|
||||
border: 1px solid rgba(0,0,0,0.12);
|
||||
text-transform: uppercase;
|
||||
line-height: 1.2;
|
||||
padding: 0.28rem 0.5rem 0;
|
||||
border: 1px solid rgba(0,0,0,0.18);
|
||||
border-bottom: none;
|
||||
border-radius: 8px 8px 0 0;
|
||||
border-radius: 0;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
z-index: 80;
|
||||
box-shadow: 0 -2px 8px rgba(0,0,0,0.12);
|
||||
box-shadow: inset 0 1px 0 rgba(255,255,255,0.35);
|
||||
}
|
||||
|
||||
.tab:hover { filter: brightness(1.06); }
|
||||
.tab-label { font-weight: 500; opacity: 0.92; }
|
||||
|
||||
.tab:hover { filter: brightness(1.04); }
|
||||
|
||||
/* Every folder same height — previews fill remaining space */
|
||||
.body {
|
||||
background: #e8e2d4;
|
||||
border: 1px solid #c4b8a8;
|
||||
border-top: none;
|
||||
border-radius: 0 10px 10px 10px;
|
||||
padding: 1.25rem 1.4rem 1.5rem;
|
||||
box-shadow: 0 10px 32px rgba(0,0,0,0.25);
|
||||
min-height: var(--folder-body-h);
|
||||
height: var(--folder-body-h);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
font-family: var(--sans);
|
||||
font-size: 0.88rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.55;
|
||||
background: linear-gradient(180deg, var(--manila) 0%, #e3dbc8 100%);
|
||||
border: 1px solid var(--manila-edge);
|
||||
border-top: 2px solid var(--manila-dark);
|
||||
border-radius: 0;
|
||||
padding: 0.85rem 1rem 0.95rem 1.15rem;
|
||||
box-shadow:
|
||||
inset 5px 0 0 var(--manila-dark),
|
||||
inset 0 -1px 0 rgba(0,0,0,0.06);
|
||||
}
|
||||
|
||||
.f0 { top: var(--stack-stick); z-index: 1; }
|
||||
.f0 .tab { top: var(--stack-stick); margin-left: calc(var(--tab-offset) * 0); background: #c9a86c; color: #2a2824; }
|
||||
.body-copy {
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.f1 { top: calc(var(--stack-stick) + var(--stack-step)); z-index: 2; }
|
||||
.f1 .tab { top: calc(var(--stack-stick) + var(--stack-step)); margin-left: calc(var(--tab-offset) * 1); background: #a8c4d4; color: #1a2830; }
|
||||
.body-copy h2 {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.f2 { top: calc(var(--stack-stick) + var(--stack-step) * 2); z-index: 3; }
|
||||
.f2 .tab { top: calc(var(--stack-stick) + var(--stack-step) * 2); margin-left: calc(var(--tab-offset) * 2); background: #b8d4a8; color: #1a2818; }
|
||||
.body-copy p:last-child {
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.f3 { top: calc(var(--stack-stick) + var(--stack-step) * 3); z-index: 4; }
|
||||
.f3 .tab { top: calc(var(--stack-stick) + var(--stack-step) * 3); margin-left: calc(var(--tab-offset) * 3); background: #d4b8c4; color: #2a1820; }
|
||||
.body-fill {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.f4 { top: calc(var(--stack-stick) + var(--stack-step) * 4); z-index: 5; }
|
||||
.f4 .tab { top: calc(var(--stack-stick) + var(--stack-step) * 4); margin-left: calc(var(--tab-offset) * 4); background: #d4c8a8; color: #2a2418; }
|
||||
/* Preview folders: text on top, screenshot fills the rest */
|
||||
.body--has-preview {
|
||||
padding-bottom: 0.7rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.f5 { top: calc(var(--stack-stick) + var(--stack-step) * 5); z-index: 6; }
|
||||
.f5 .tab { top: calc(var(--stack-stick) + var(--stack-step) * 5); margin-left: calc(var(--tab-offset) * 5); background: #c4c4c4; color: #2a2a2a; }
|
||||
.body--has-preview .body-copy {
|
||||
max-height: 38%;
|
||||
overflow: hidden;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.f6 { top: calc(var(--stack-stick) + var(--stack-step) * 6); z-index: 7; margin-bottom: 4rem; }
|
||||
.f6 .tab { top: calc(var(--stack-stick) + var(--stack-step) * 6); margin-left: calc(var(--tab-offset) * 6); background: #2a4a6b; color: #e8e2d4; }
|
||||
.body--has-preview .body-copy p {
|
||||
margin-bottom: 0.3rem;
|
||||
font-size: 0.86rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.body--has-preview .body-copy h1 {
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
|
||||
.body--has-preview .body-copy h2 {
|
||||
font-size: 1.05rem;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
|
||||
.body--has-preview .body-copy .cta-block {
|
||||
margin-top: 0.35rem;
|
||||
}
|
||||
|
||||
.body--has-preview .terms-list {
|
||||
margin-bottom: 0.25rem;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.body--has-preview .terms-list li {
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
|
||||
/* Screenshot preview — click opens site in new tab */
|
||||
a.site-preview {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
margin: 0.4rem 0 0;
|
||||
border: 1px solid var(--manila-edge);
|
||||
background: #f6f2e8;
|
||||
box-shadow: inset 0 0 0 1px rgba(255,255,255,0.5);
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
a.site-preview:hover {
|
||||
border-color: #a89878;
|
||||
filter: brightness(1.02);
|
||||
}
|
||||
|
||||
.site-preview-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
padding: 0.3rem 0.45rem;
|
||||
background: #ddd6c8;
|
||||
border-bottom: 1px solid var(--manila-edge);
|
||||
font-family: var(--mono);
|
||||
font-size: 0.52rem;
|
||||
color: var(--ink-muted);
|
||||
}
|
||||
|
||||
.site-preview-url {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.site-preview-go {
|
||||
color: #2a4a6b;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.site-preview-frame {
|
||||
display: flex;
|
||||
flex: 1 1 0;
|
||||
min-height: 11rem;
|
||||
overflow: hidden;
|
||||
background: #1a1814;
|
||||
}
|
||||
|
||||
.site-preview-img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
object-fit: cover;
|
||||
object-position: top center;
|
||||
background: #1a1814;
|
||||
}
|
||||
|
||||
.cta-block {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.45rem;
|
||||
margin: 0.55rem 0 0.35rem;
|
||||
}
|
||||
|
||||
.body .btn {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.68rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.body .btn--primary {
|
||||
background: #2a4a6b;
|
||||
color: #fff !important;
|
||||
border: 1px solid #1e3854;
|
||||
}
|
||||
|
||||
.body .btn--primary:hover {
|
||||
background: #1e3854;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.body .btn--ghost {
|
||||
background: #f6f2e8;
|
||||
color: #1c3a5c !important;
|
||||
border: 1px solid #8a9ab0;
|
||||
}
|
||||
|
||||
.body .btn--ghost:hover {
|
||||
background: #fff;
|
||||
color: #2a4a6b !important;
|
||||
}
|
||||
|
||||
.service-deep-list {
|
||||
margin: 0 0 0.5rem 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.4;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.service-deep-list > li {
|
||||
margin-bottom: 0.55rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.service-deep-list > li:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.service-deep-list strong {
|
||||
display: inline;
|
||||
font-family: var(--sans);
|
||||
font-size: inherit;
|
||||
font-weight: 600;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.hi {
|
||||
font-weight: 600;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.service-deep-list .xref {
|
||||
display: block;
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
|
||||
.section-lead {
|
||||
font-size: 0.88rem;
|
||||
color: var(--ink-muted);
|
||||
margin-bottom: 0.55rem;
|
||||
}
|
||||
|
||||
/* L2 — dense copy, no internal scroll */
|
||||
.folder--services .body-copy {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.folder--services .section-lead {
|
||||
font-size: 0.86rem;
|
||||
margin-bottom: 0.4rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.folder--services .service-deep-list {
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.folder--services .service-deep-list > li {
|
||||
margin-bottom: 0.35rem;
|
||||
padding-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
.folder--services .service-deep-list strong {
|
||||
font-size: 0.88rem;
|
||||
display: inline;
|
||||
margin-right: 0.2rem;
|
||||
}
|
||||
|
||||
.folder--services .service-deep-list .xref {
|
||||
display: inline;
|
||||
margin-top: 0;
|
||||
margin-left: 0.15rem;
|
||||
}
|
||||
|
||||
.folder--services .body-copy > p:last-child {
|
||||
font-size: 0.78rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.body::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 28%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
background: linear-gradient(90deg, transparent, rgba(0,0,0,0.03));
|
||||
}
|
||||
|
||||
/* Bodies pile by z-index; each tab sticks above its folder */
|
||||
.f0 .body { z-index: 1; }
|
||||
.f1 .body { z-index: 2; }
|
||||
.f2 .body { z-index: 3; }
|
||||
.f3 .body { z-index: 4; }
|
||||
.f4 .body { z-index: 5; }
|
||||
.f5 .body { z-index: 6; }
|
||||
.f6 .body { z-index: 7; }
|
||||
.f7 .body { z-index: 8; }
|
||||
|
||||
.f0 .tab { margin-left: calc(var(--tab-offset) * 0); z-index: 110; background: var(--l0-tab); color: var(--l0-tab-fg); }
|
||||
.f1 .tab { margin-left: calc(var(--tab-offset) * 1); z-index: 111; background: var(--l1-tab); color: var(--l1-tab-fg); }
|
||||
.f2 .tab { margin-left: calc(var(--tab-offset) * 2); z-index: 112; background: var(--l2-tab); color: var(--l2-tab-fg); }
|
||||
.f3 .tab { margin-left: calc(var(--tab-offset) * 3); z-index: 113; background: var(--l3-tab); color: var(--l3-tab-fg); }
|
||||
.f4 .tab { margin-left: calc(var(--tab-offset) * 4); z-index: 114; background: var(--l4-tab); color: var(--l4-tab-fg); }
|
||||
.f5 .tab { margin-left: calc(var(--tab-offset) * 5); z-index: 115; background: var(--l5-tab); color: var(--l5-tab-fg); }
|
||||
.f6 .tab { margin-left: calc(var(--tab-offset) * 6); z-index: 116; background: var(--l6-tab); color: var(--l6-tab-fg); }
|
||||
.f7 .tab { margin-left: calc(var(--tab-offset) * 7); z-index: 117; background: var(--l7-tab); color: var(--l7-tab-fg); }
|
||||
|
||||
.file-id {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.58rem;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--ink-muted);
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.body h1 {
|
||||
font-family: var(--display);
|
||||
font-size: 1.35rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.25;
|
||||
margin-bottom: 0.35rem;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.body h2 {
|
||||
font-family: var(--display);
|
||||
font-size: 1.05rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.4rem;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.lead, .body p, .clause {
|
||||
font-size: 0.88rem;
|
||||
color: var(--ink-muted);
|
||||
line-height: 1.55;
|
||||
margin-bottom: 0.45rem;
|
||||
}
|
||||
|
||||
.stack-note, .xref {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.68rem;
|
||||
color: var(--ink-muted);
|
||||
}
|
||||
|
||||
.body a:not(.btn) { color: #2a4a6b; font-weight: 600; }
|
||||
|
||||
.meta-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.8rem;
|
||||
margin: 0.5rem 0 0.65rem;
|
||||
}
|
||||
|
||||
.meta-table th {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.62rem;
|
||||
font-weight: 500;
|
||||
text-align: left;
|
||||
color: var(--ink-muted);
|
||||
padding: 0.2rem 0.65rem 0.2rem 0;
|
||||
vertical-align: top;
|
||||
width: 5.5rem;
|
||||
}
|
||||
|
||||
.meta-table td { padding: 0.2rem 0; color: var(--ink); }
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
font-family: var(--mono);
|
||||
font-size: 0.58rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.08em;
|
||||
padding: 0.12rem 0.4rem;
|
||||
border: 1px solid var(--ink);
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.badge--avail { border-color: #3d6b3d; color: #2d5a2d; }
|
||||
|
||||
.avail { margin-top: 0.35rem; font-size: 0.82rem; }
|
||||
|
||||
.terms-list {
|
||||
margin: 0 0 0.65rem 1rem;
|
||||
font-size: 1.05rem;
|
||||
color: var(--ink);
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.terms-list strong {
|
||||
font-weight: 600;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.terms-list li { margin-bottom: 0.35rem; }
|
||||
|
||||
.meta-table--contact { margin-top: 0.5rem; }
|
||||
|
||||
.body h1 { font-size: 1.65rem; margin-bottom: 0.35rem; }
|
||||
.body h2 { font-size: 1.25rem; margin-bottom: 0.4rem; }
|
||||
.body p { font-size: 0.92rem; color: #4a4844; line-height: 1.5; }
|
||||
.body a { color: #2a4a6b; }
|
||||
.avail { font-family: var(--mono); font-size: 0.68rem; color: #3d6b3d; margin-top: 0.5rem; }
|
||||
.btn {
|
||||
font-family: var(--mono); font-size: 0.7rem; padding: 0.45rem 0.75rem;
|
||||
background: #2a4a6b; color: #fff; text-decoration: none; border-radius: 4px;
|
||||
margin-right: 0.4rem; display: inline-block; margin-top: 0.35rem;
|
||||
padding: 0.5rem 0.85rem;
|
||||
text-decoration: none;
|
||||
border-radius: 0;
|
||||
margin-right: 0.35rem;
|
||||
display: inline-block;
|
||||
margin-top: 0.3rem;
|
||||
}
|
||||
.btn.ghost { background: transparent; color: #2a4a6b; border: 1px solid #2a4a6b; }
|
||||
|
||||
.foot {
|
||||
display: flex; justify-content: space-between;
|
||||
width: min(640px, 100%); margin: 0 auto;
|
||||
padding: 0 1rem 2rem; font-family: var(--mono); font-size: 0.62rem; color: #6a6458;
|
||||
/* Cal.com embed (needs embed allowlist on cal.levkin.ca for levkin.ca) */
|
||||
.cal-slot {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
margin: 0.4rem 0 0;
|
||||
border: 1px solid var(--manila-edge);
|
||||
background: #1a1814;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cal-slot .site-preview-bar {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
padding: 0.3rem 0.45rem;
|
||||
background: #ddd6c8;
|
||||
border-bottom: 1px solid var(--manila-edge);
|
||||
font-family: var(--mono);
|
||||
font-size: 0.52rem;
|
||||
color: var(--ink-muted);
|
||||
}
|
||||
|
||||
.cal-slot .site-preview-go {
|
||||
color: #2a4a6b;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.cal-embed-frame {
|
||||
position: relative;
|
||||
flex: 1 1 auto;
|
||||
min-height: 11rem;
|
||||
background: #0a0a0a;
|
||||
}
|
||||
|
||||
.cal-slot[data-cal-theme='light'] .cal-embed-frame {
|
||||
background: #f5f3ee;
|
||||
}
|
||||
|
||||
.cal-inline {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 14rem;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.cal-slot.is-blocked .cal-inline {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.cal-slot.is-embedded .cal-inline iframe {
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-height: 14rem;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.cal-embed-fallback {
|
||||
display: none;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.cal-slot.is-blocked .cal-embed-fallback {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.cal-embed-fallback img {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
object-position: top center;
|
||||
/* Crop Cal.com branding bar at bottom of screenshot */
|
||||
max-height: calc(100% + 2rem);
|
||||
margin-bottom: -2rem;
|
||||
}
|
||||
|
||||
.cal-embed-fallback-label {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.55rem;
|
||||
text-align: center;
|
||||
padding: 0.35rem;
|
||||
background: rgba(0, 0, 0, 0.75);
|
||||
color: #c4b8a8;
|
||||
}
|
||||
|
||||
.service-note {
|
||||
font-size: 0.78rem;
|
||||
line-height: 1.4;
|
||||
color: var(--ink-muted);
|
||||
margin-bottom: 0.28rem;
|
||||
}
|
||||
|
||||
.folder--services .service-deep-list > li {
|
||||
margin-bottom: 0.28rem;
|
||||
padding-bottom: 0.22rem;
|
||||
}
|
||||
|
||||
.folder--services .body-copy > p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.foot a { color: #6a6458; text-decoration: none; }
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.tab-rail { display: none; }
|
||||
:root { --stack-step: 1.75rem; --tab-offset: 1.4rem; --stack-card-min: 48vh; }
|
||||
.f5 .tab { margin-left: calc(var(--tab-offset) * 4); }
|
||||
.f6 .tab { margin-left: calc(var(--tab-offset) * 3); }
|
||||
.mount { padding-right: 1rem; }
|
||||
:root { --tab-offset: 1.75rem; }
|
||||
}
|
||||
|
||||
|
||||
@@ -3,21 +3,21 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Levkin — Stack Folder</title>
|
||||
<title>Levkin — Company Files</title>
|
||||
<meta name="description" content="Levkin Inc. — company specification, filed by section." />
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&family=Instrument+Sans:wght@400;500;600&display=swap" rel="stylesheet" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&family=Instrument+Sans:wght@400;500;600&family=Literata:opsz,wght@7..72,400;7..72,600&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="./folder.css" />
|
||||
</head>
|
||||
<body>
|
||||
<nav class="nav">
|
||||
<a href="/">← options</a>
|
||||
<a href="/stack/">cards</a>
|
||||
<a href="/stack-trace/">trace</a>
|
||||
<a href="/stack-rack/">rack</a>
|
||||
<span class="depth" id="depth">L0</span>
|
||||
</nav>
|
||||
<header class="site-header desk-footer">
|
||||
Levkin Inc. · production software, automation, and handoff-ready systems ·
|
||||
<a href="https://cal.levkin.ca/ilia/consult" target="_blank" rel="noopener noreferrer">book discovery call</a>
|
||||
</header>
|
||||
|
||||
<div class="tab-rail" aria-label="Jump to layer">
|
||||
<div class="tab-rail" id="tab-rail" aria-label="Layers L0–L7">
|
||||
<button type="button" class="rail-tab active" data-goto="0" data-layer="0">L0</button>
|
||||
<button type="button" class="rail-tab" data-goto="1" data-layer="1">L1</button>
|
||||
<button type="button" class="rail-tab" data-goto="2" data-layer="2">L2</button>
|
||||
@@ -25,74 +25,161 @@
|
||||
<button type="button" class="rail-tab" data-goto="4" data-layer="4">L4</button>
|
||||
<button type="button" class="rail-tab" data-goto="5" data-layer="5">L5</button>
|
||||
<button type="button" class="rail-tab" data-goto="6" data-layer="6">L6</button>
|
||||
<button type="button" class="rail-tab" data-goto="7" data-layer="7">L7</button>
|
||||
</div>
|
||||
|
||||
<main class="mount">
|
||||
<article class="folder f0" data-layer="0" id="layer-0">
|
||||
<button type="button" class="tab" data-goto="0">L0 · foundation</button>
|
||||
<div class="body">
|
||||
<h1>Levkin</h1>
|
||||
<p>Software development · Canada · remote</p>
|
||||
<p class="avail">Taking new engagements · 15+ yrs · 8h→2m</p>
|
||||
<button type="button" class="tab" data-goto="0"><span class="tab-code">L0</span><span class="tab-label"> · Company</span></button>
|
||||
<div class="body body--has-preview">
|
||||
<div class="body-copy">
|
||||
<p class="file-id">LK-SPEC-1.0 · Cover sheet</p>
|
||||
<h1>Levkin Software Development Company</h1>
|
||||
<p class="lead">Custom applications, automation, and practice systems — <span class="hi">scoped</span>, <span class="hi">documented</span>, and built to run in <span class="hi">production</span>.</p>
|
||||
<p class="avail"><span class="badge badge--avail">AVAILABLE</span> <span class="hi">Taking on new engagements</span> · remote NA & EU</p>
|
||||
<div class="cta-block">
|
||||
<a class="btn btn--primary" href="https://cal.levkin.ca/ilia/consult" target="_blank" rel="noopener noreferrer">Book 15-minute discovery call</a>
|
||||
<a class="btn btn--ghost" href="mailto:ilia@levkine.ca?subject=Project%20enquiry">ilia@levkine.ca</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cal-slot is-blocked" data-cal-embed data-cal-theme="dark" data-cal-target="cal-inline-l0">
|
||||
<div class="site-preview-bar"><span class="site-preview-url">cal.levkin.ca · book a call</span><a class="site-preview-go" href="https://cal.levkin.ca/ilia/consult" target="_blank" rel="noopener noreferrer" aria-label="Open calendar in new tab">↗</a></div>
|
||||
<div class="cal-embed-frame">
|
||||
<div class="cal-inline" id="cal-inline-l0"></div>
|
||||
<a class="cal-embed-fallback" href="https://cal.levkin.ca/ilia/consult" target="_blank" rel="noopener noreferrer">
|
||||
<img src="./previews/cal-dark.png" alt="" decoding="async" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="folder f1" data-layer="1" id="layer-1">
|
||||
<button type="button" class="tab" data-goto="1">L1 · application</button>
|
||||
<div class="body">
|
||||
<h2>Custom software</h2>
|
||||
<p>Web apps, APIs, tools — TypeScript · Python · .NET · PostgreSQL</p>
|
||||
<button type="button" class="tab" data-goto="1"><span class="tab-code">L1</span><span class="tab-label"> · Scope</span></button>
|
||||
<div class="body body--has-preview">
|
||||
<div class="body-copy">
|
||||
<h2>Scope</h2>
|
||||
<p><span class="hi">Boutique practice</span> — not a staffing agency. Clear deliverable and handoff so your team can own what ships.</p>
|
||||
</div>
|
||||
<a class="site-preview" href="/spec/" target="_blank" rel="noopener noreferrer">
|
||||
<span class="site-preview-bar"><span class="site-preview-url">levkin.ca / spec</span><span class="site-preview-go" aria-hidden="true">↗</span></span>
|
||||
<span class="site-preview-frame"><img class="site-preview-img" src="./previews/spec.png" alt="" loading="lazy" decoding="async" /></span>
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="folder f2" data-layer="2" id="layer-2">
|
||||
<button type="button" class="tab" data-goto="2">L2 · automation</button>
|
||||
<article class="folder f2 folder--services" data-layer="2" id="layer-2">
|
||||
<button type="button" class="tab" data-goto="2"><span class="tab-code">L2</span><span class="tab-label"> · Services</span></button>
|
||||
<div class="body">
|
||||
<h2>Automation</h2>
|
||||
<p>n8n · Zapier · CI/CD · LLMs · <a href="https://auto.levkin.ca">auto.levkin.ca</a></p>
|
||||
<div class="body-copy">
|
||||
<h2>Services</h2>
|
||||
<p class="section-lead">Primary lines — each engagement gets a <span class="hi">statement of work</span> after discovery.</p>
|
||||
<ul class="service-deep-list">
|
||||
<li><strong>Custom software</strong> — Web apps, APIs, and internal tools; fit the problem, not a preset stack.</li>
|
||||
<li><strong>Automation</strong> — Reporting, notifications, data sync, ops in the background. <span class="xref"><a href="https://auto.levkin.ca" target="_blank" rel="noopener noreferrer">auto.levkin.ca</a></span></li>
|
||||
<li><strong>CaseWare</strong> — Templates, releases, practice customization, pipeline work. <span class="xref"><a href="https://caseware.levkin.ca" target="_blank" rel="noopener noreferrer">caseware.levkin.ca</a></span></li>
|
||||
<li><strong>Quality & testing</strong> — Test strategy, automation, release confidence. <span class="xref"><a href="https://iliadobkin.com" target="_blank" rel="noopener noreferrer">iliadobkin.com</a></span></li>
|
||||
</ul>
|
||||
<p class="service-note"><strong>How we work:</strong> fixed scope · production-ready · handoff-friendly · right-sized solutions.</p>
|
||||
<p class="service-note">Discovery call → written proposal → delivery with docs → optional support.</p>
|
||||
</div>
|
||||
<div class="body-fill" aria-hidden="true"></div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="folder f3" data-layer="3" id="layer-3">
|
||||
<button type="button" class="tab" data-goto="3">L3 · enterprise</button>
|
||||
<div class="body">
|
||||
<h2>CaseWare & CaseView</h2>
|
||||
<p>15+ years · CaseWare Intl, MNP, JazzIt · <a href="https://caseware.levkin.ca">caseware.levkin.ca</a></p>
|
||||
<button type="button" class="tab" data-goto="3"><span class="tab-code">L3</span><span class="tab-label"> · Automation</span></button>
|
||||
<div class="body body--has-preview">
|
||||
<div class="body-copy">
|
||||
<h2>Automation</h2>
|
||||
<p>Workflows that run <span class="hi">reliably in the background</span> — reporting, sync, ops off your plate.</p>
|
||||
</div>
|
||||
<a class="site-preview" href="https://auto.levkin.ca" target="_blank" rel="noopener noreferrer">
|
||||
<span class="site-preview-bar"><span class="site-preview-url">auto.levkin.ca</span><span class="site-preview-go" aria-hidden="true">↗</span></span>
|
||||
<span class="site-preview-frame"><img class="site-preview-img" src="./previews/auto.png" alt="" loading="lazy" decoding="async" /></span>
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="folder f4" data-layer="4" id="layer-4">
|
||||
<button type="button" class="tab" data-goto="4">L4 · quality</button>
|
||||
<div class="body">
|
||||
<h2>Quality engineering</h2>
|
||||
<p>Senior SDET · <a href="https://iliadobkin.com">iliadobkin.com</a></p>
|
||||
<button type="button" class="tab" data-goto="4"><span class="tab-code">L4</span><span class="tab-label"> · Enterprise</span></button>
|
||||
<div class="body body--has-preview">
|
||||
<div class="body-copy">
|
||||
<h2>CaseWare</h2>
|
||||
<p>Templates, releases, and practice customization — from small fixes to full pipeline overhauls.</p>
|
||||
</div>
|
||||
<a class="site-preview" href="https://caseware.levkin.ca" target="_blank" rel="noopener noreferrer">
|
||||
<span class="site-preview-bar"><span class="site-preview-url">caseware.levkin.ca</span><span class="site-preview-go" aria-hidden="true">↗</span></span>
|
||||
<span class="site-preview-frame"><img class="site-preview-img" src="./previews/caseware.png" alt="" loading="lazy" decoding="async" /></span>
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="folder f5" data-layer="5" id="layer-5">
|
||||
<button type="button" class="tab" data-goto="5">L5 · operations</button>
|
||||
<div class="body">
|
||||
<h2>Job Ops</h2>
|
||||
<p>Internal hiring orchestrator · <a href="https://jobs.levkin.ca">jobs.levkin.ca</a></p>
|
||||
<button type="button" class="tab" data-goto="5"><span class="tab-code">L5</span><span class="tab-label"> · Portfolio</span></button>
|
||||
<div class="body body--has-preview">
|
||||
<div class="body-copy">
|
||||
<h2>Quality & testing</h2>
|
||||
<p>Test strategy, automation, and release confidence without hiring full-time.</p>
|
||||
</div>
|
||||
<a class="site-preview" href="https://iliadobkin.com" target="_blank" rel="noopener noreferrer">
|
||||
<span class="site-preview-bar"><span class="site-preview-url">iliadobkin.com</span><span class="site-preview-go" aria-hidden="true">↗</span></span>
|
||||
<span class="site-preview-frame"><img class="site-preview-img" src="./previews/iliadobkin.png" alt="" loading="lazy" decoding="async" /></span>
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="folder f6" data-layer="6" id="layer-6">
|
||||
<button type="button" class="tab" data-goto="6">L6 · interface</button>
|
||||
<div class="body">
|
||||
<h2>Engage</h2>
|
||||
<p>Discover → Proposal → Ship → Maintain</p>
|
||||
<p><a class="btn" href="https://cal.levkin.ca/ilia/consult">Book 15 min</a> <a class="btn ghost" href="mailto:hello@levkine.ca">hello@levkine.ca</a></p>
|
||||
<button type="button" class="tab" data-goto="6"><span class="tab-code">L6</span><span class="tab-label"> · Source</span></button>
|
||||
<div class="body body--has-preview">
|
||||
<div class="body-copy">
|
||||
<h2>Source & repos</h2>
|
||||
<p>Project code and templates on our Gitea — private by default, yours on handoff.</p>
|
||||
</div>
|
||||
<a class="site-preview" href="https://git.levkin.ca/explore/repos" target="_blank" rel="noopener noreferrer">
|
||||
<span class="site-preview-bar"><span class="site-preview-url">git.levkin.ca / explore</span><span class="site-preview-go" aria-hidden="true">↗</span></span>
|
||||
<span class="site-preview-frame"><img class="site-preview-img" src="./previews/git-repos.png" alt="" loading="lazy" decoding="async" /></span>
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<div class="stack-stop"></div>
|
||||
</main>
|
||||
<article class="folder f7 folder--last" data-layer="7" id="layer-7">
|
||||
<button type="button" class="tab" data-goto="7"><span class="tab-code">L7</span><span class="tab-label"> · Engage</span></button>
|
||||
<div class="body body--has-preview">
|
||||
<div class="body-copy">
|
||||
<h2>Terms & contact</h2>
|
||||
<ul class="terms-list">
|
||||
<li><strong>Fixed scope</strong> — quoted after discovery; no open-ended hourly surprises.</li>
|
||||
<li><strong>Production-ready</strong> — monitoring, failure handling, documentation included.</li>
|
||||
<li><strong>Handoff-friendly</strong> — your team or the next vendor can maintain what we ship.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="cal-slot is-blocked" data-cal-embed data-cal-theme="light" data-cal-target="cal-inline-l7">
|
||||
<div class="site-preview-bar"><span class="site-preview-url">cal.levkin.ca · book a call</span><a class="site-preview-go" href="https://cal.levkin.ca/ilia/consult" target="_blank" rel="noopener noreferrer" aria-label="Open calendar in new tab">↗</a></div>
|
||||
<div class="cal-embed-frame">
|
||||
<div class="cal-inline" id="cal-inline-l7"></div>
|
||||
<a class="cal-embed-fallback" href="https://cal.levkin.ca/ilia/consult" target="_blank" rel="noopener noreferrer">
|
||||
<img src="./previews/cal-light.png" alt="" decoding="async" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<footer class="foot"><a href="https://git.levkin.ca">git.levkin.ca</a><span>levkin.ca</span></footer>
|
||||
<div class="stack-stop" aria-hidden="true"></div>
|
||||
</main>
|
||||
<script type="module">
|
||||
import { initStackScroll } from '../shared/stack-scroll.js';
|
||||
initStackScroll();
|
||||
import { initRailRoller } from './folder-rail.js';
|
||||
import { initCalEmbeds } from './folder-cal.js';
|
||||
initStackScroll({
|
||||
sectionSelector: '.folder[data-layer]',
|
||||
mountSelector: '.mount',
|
||||
foldTabs: true,
|
||||
interactionMode: 'navigate',
|
||||
});
|
||||
initRailRoller();
|
||||
initCalEmbeds();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 180 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 167 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 74 KiB |