diff --git a/.Jules/palette.md b/.Jules/palette.md index 0b52d47..2641074 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -45,3 +45,26 @@ ## 2026-03-01 - External Links and Interactive Icons **Learning:** Setting `aria-hidden="true"` on custom external link icons prevents screen readers from announcing that the link opens in a new tab. Additionally, using only `hover` classes on interactive icons within a link omits keyboard users from seeing the same visual interactions. **Action:** Always assign `role="img"` and `aria-label="(opens in new tab)"` to SVG icons indicating external links. Furthermore, apply equivalent `focus-visible` classes to any hover interactions inside interactive elements to ensure visual feedback parity for keyboard users. + +## 2026-03-02 - Dynamic Link Attributes + +**Learning:** When dynamically rendering `` components, simply inserting attributes like `target="_blank"` or `aria-label` inside a loop can accidentally pollute internal links with external behaviors or unnecessary labels. +**Action:** Use an object spread with a conditional (e.g., `{...(isExternal ? { target: "_blank", rel: "noopener noreferrer", "aria-label": \`\${title} (opens in a new tab)\`, title: title } : {})}`) to apply specific accessibility and behavioral attributes only when appropriate, keeping the DOM clean and accessible. + +## 2026-04-26 - Hardcoded Heading Levels in Reusable Components +**Learning:** Hardcoding specific heading levels (like `

`) inside reusable UI components (like cards) often breaks semantic document structure when the component is placed in different contexts on a page, causing screen readers to skip levels or announce confusing hierarchies. +**Action:** Always ensure nested headings (like card titles) properly increment relative to their parent container's heading level, or consider passing the appropriate heading level as a prop to the component to maintain strict HTML semantics. +## 2024-05-03 - Redundant Link Icons Announcement +**Learning:** Decorative icons indicating external or internal links (like `↗`) that are appended to already readable links cause frustrating redundant screen reader announcements (e.g., reading "North East Arrow" immediately after reading the link text) and create confusing double tab-stops if wrapped in separate interactive elements. +**Action:** Always add `aria-hidden="true"` to decorative link icons or elements. If an icon must be inside its own link but points to the same destination as a preceding adjacent text link, also add `tabIndex="-1"` to prevent it from receiving keyboard focus, streamlining navigation. + +## 2026-05-10 - Expanding Hit Areas for Block Elements +**Learning:** Having only text as the clickable area within a larger logical block (like a project listing or card) violates Fitts's Law and frustrates mobile users who try to tap the area around the text. +**Action:** Always wrap the entire logical block in a `` (or `` tag) and apply `display: block` to expand the interactive hit area. Pair this with hover and `:focus-visible` styles on the entire block, and use transforms on trailing icons (e.g., `↗`) to provide clear visual affordance. + +## 2026-05-18 - Expanding Hit Areas for Table Rows +**Learning:** Having only text as the clickable area within a table row violates Fitts's Law. Wrapping an entire `` in an `` or `` tag is invalid HTML and breaks semantic document structure. +**Action:** Use relative positioned rows (`position: relative` on `tr`) and absolute positioned `::after` pseudo-elements (`inset: 0; position: absolute; z-index: 1`) on the primary link to safely expand the interactive hit area. Pair this with `:focus-within` styles on the entire row and `z-index: 2` on secondary interactive elements within the row. +## 2026-05-31 - Hiding Redundant Directional Arrows +**Learning:** Text-based directional arrows (like `→` and `↗`) used inline for visual affordance are read out loud by screen readers, creating annoying auditory clutter (e.g., reading "Start an inquiry rightwards arrow"). +**Action:** Always wrap text-based decorative arrows in `

+
+
+
5l-labs.com
+
+
+
+
+ ); +} diff --git a/src/components/HomepageRedesign/Manifesto.jsx b/src/components/HomepageRedesign/Manifesto.jsx new file mode 100644 index 0000000..92124e4 --- /dev/null +++ b/src/components/HomepageRedesign/Manifesto.jsx @@ -0,0 +1,106 @@ +import React from 'react'; +import { Nav, Footer, Note, PH, Lines } from './shared'; + +export default function Manifesto() { + return ( +
+
+
+
5l-labs.com
+
+
+
+
+ ); +} diff --git a/src/components/HomepageRedesign/PreviewApp.jsx b/src/components/HomepageRedesign/PreviewApp.jsx new file mode 100644 index 0000000..5b055d4 --- /dev/null +++ b/src/components/HomepageRedesign/PreviewApp.jsx @@ -0,0 +1,176 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { DesignCanvas, DCSection, DCArtboard } from './DesignCanvas'; +import Manifesto from './Manifesto'; +import Journal from './Journal'; +import Terminal from './Terminal'; +import Schematic from './Schematic'; +import './sketch.css'; + +const LS_VIEW = '5l-wireframe-view'; + +const TWEAK_DEFAULTS = { + density: 'roomy', + accent: '#1e5f8a', + notes: 'show', +}; + +const ACCENT_SWATCHES = ['#c84a1f', '#1e5f8a', '#2a7d4f', '#1a1a1a']; + +function Toolbar({ view, onSetView }) { + const buttons = [ + { id: 'canvas', label: 'All · canvas' }, + { id: '1', label: '1 · Manifesto' }, + { id: '2', label: '2 · Journal' }, + { id: '3', label: '3 · Terminal' }, + { id: '4', label: '4 · Schematic' }, + ]; + return ( +
+ VIEW + {buttons.map((b, i) => ( + + {i === 1 &&
} + + + ))} +
+ ); +} + +function TweaksPanel({ tweaks, onTweak }) { + return ( +
+
TWEAKS
+ +
+ +
+ {['tight', 'roomy'].map(v => ( + + ))} +
+
+ +
+ +
+ {ACCENT_SWATCHES.map(c => ( +
+
+ +
+ +
+ {['show', 'hide'].map(v => ( + + ))} +
+
+
+ ); +} + +function CanvasView() { + return ( + + + + + + + + + + + + + + + + + ); +} + +function SingleView({ which }) { + const components = { '1': Manifesto, '2': Journal, '3': Terminal, '4': Schematic }; + const Comp = components[which]; + return ( +
+
+ +
+
+ ); +} + +export default function PreviewApp() { + const rootRef = useRef(null); + + const [view, setView] = useState(() => { + try { return localStorage.getItem(LS_VIEW) || 'canvas'; } catch { return 'canvas'; } + }); + + const [tweaks, setTweaks] = useState(TWEAK_DEFAULTS); + + const handleSetView = (v) => { + try { localStorage.setItem(LS_VIEW, v); } catch {} + setView(v); + }; + + const handleTweak = (key, value) => { + setTweaks(prev => ({ ...prev, [key]: value })); + }; + + // Apply tweaks via CSS custom properties and class toggles on the root element + useEffect(() => { + const el = rootRef.current; + if (!el) return; + el.style.setProperty('--accent', tweaks.accent); + el.style.setProperty('--wf-accent', tweaks.accent); + el.classList.toggle('roomy', tweaks.density === 'roomy'); + el.classList.toggle('notes-hidden', tweaks.notes === 'hide'); + }, [tweaks]); + + const rootClasses = [ + 'wf-preview-root', + tweaks.density === 'roomy' ? 'roomy' : '', + tweaks.notes === 'hide' ? 'notes-hidden' : '', + ].filter(Boolean).join(' '); + + return ( +
+ + + {view === 'canvas' ? : } + + {view === 'canvas' && ( +
+ 5L Labs — Homepage wireframes +
4 rough directions · low-fi, b&w + rust accent.
+
Click a tab above to zoom into one.
+
+ )} + + +
+ ); +} diff --git a/src/components/HomepageRedesign/Schematic.jsx b/src/components/HomepageRedesign/Schematic.jsx new file mode 100644 index 0000000..ceb838f --- /dev/null +++ b/src/components/HomepageRedesign/Schematic.jsx @@ -0,0 +1,142 @@ +import React from 'react'; +import { Nav, Footer, Note } from './shared'; + +export default function Schematic() { + return ( +
+
+
+
5l-labs.com
+
+
+
+
+ ); +} diff --git a/src/components/HomepageRedesign/Terminal.jsx b/src/components/HomepageRedesign/Terminal.jsx new file mode 100644 index 0000000..5c09b48 --- /dev/null +++ b/src/components/HomepageRedesign/Terminal.jsx @@ -0,0 +1,129 @@ +import React from 'react'; +import { Nav, Footer, Note } from './shared'; + +const entries = [ + ['2026-02-23', 'writing', 'frontier', 'learning about learning?', '14 min'], + ['2026-02-10', 'release', 'applied-ai', 'open-embeddings v0.4.0', 'changelog'], + ['2026-01-28', 'writing', 'applied-ai', 'benchmarking private embeddings vs ada-002', '8 min'], + ['2026-01-14', 'project', 'iot', 'overlord-network-kill-switch v1.2', 'hardware'], + ['2025-12-30', 'writing', 'iot', 'flashing tasmota on a $4 zigbee hub', '6 min'], + ['2025-12-10', 'release', 'applied-ai', 'recruiter-rankings preview open', 'preview'], + ['2025-11-22', 'writing', 'frontier', 'differential privacy without the accuracy cliff', '11 min'], +]; + +export default function Terminal() { + return ( +
+
+
+
5l-labs.com
+
+
+
+
+ ); +} diff --git a/src/components/HomepageRedesign/shared.jsx b/src/components/HomepageRedesign/shared.jsx new file mode 100644 index 0000000..7bc287a --- /dev/null +++ b/src/components/HomepageRedesign/shared.jsx @@ -0,0 +1,94 @@ +import React from 'react'; + +export function Note({ top, left, right, bottom, children, arrow }) { + return ( +
+ {children} + {arrow && ( + + + + + )} +
+ ); +} + +export function PH({ w, h, label, style }) { + return
{label}
; +} + +export function Lines({ rows = 3, widths }) { + const ws = widths || ['w-90', 'w-80', 'w-60']; + return ( +
+ {Array.from({ length: rows }).map((_, i) => ( +
+ ))} +
+ ); +} + +export function Nav({ active, compact }) { + const links = compact + ? ['Research', 'Products', 'Consulting', 'GitHub'] + : ['Research', 'Products', 'Writing', 'Consulting', 'GitHub']; + return ( +
+
+ 5L + 5L Labs +
+
+ {links.map(l => ( + {l} + ))} +
+
+ ); +} + +export function Footer() { + return ( +
+
+
5L Labs
+
+ Commercial privacy-first.
+ Advancing technology for humans and bots. +
+
+
+
Research
+
    +
  • Private AI/ML
  • +
  • Private IoT
  • +
  • Frontier
  • +
+
+
+
Projects
+
    +
  • Open Embeddings
  • +
  • Recruiter Rankings
  • +
  • Kill Switch
  • +
+
+
+
Contact
+
    +
  • inquiries@5l-labs.com
  • +
  • GitHub
  • +
  • LinkedIn
  • +
+
+
+ ); +} diff --git a/src/components/HomepageRedesign/sketch.css b/src/components/HomepageRedesign/sketch.css new file mode 100644 index 0000000..151fa49 --- /dev/null +++ b/src/components/HomepageRedesign/sketch.css @@ -0,0 +1,415 @@ +/* + * Sketchy wireframe look — handwritten but readable, b&w + one accent + * Variables scoped to .wf-preview-root to avoid conflicts with site globals. + */ + +@import url('https://fonts.googleapis.com/css2?family=Architects+Daughter&family=Kalam:wght@400;700&family=Caveat:wght@500;700&family=JetBrains+Mono:wght@400;500&display=swap'); + +.wf-preview-root { + --wf-ink: #1a1a1a; + --wf-ink-2: #333; + --wf-ink-3: #666; + --wf-ink-4: #999; + --wf-paper: #fbfaf6; + --wf-paper-2: #f3f1ea; + --wf-accent: #c84a1f; + --wf-accent-soft: #fde3d6; + --wf-yellow: #ffec8a; + --wf-hand: 'Kalam', 'Caveat', cursive; + --wf-hand-tight: 'Architects Daughter', 'Kalam', cursive; + --wf-mono: 'JetBrains Mono', ui-monospace, monospace; + + /* Aliases used in inline styles */ + --ink: var(--wf-ink); + --ink-2: var(--wf-ink-2); + --ink-3: var(--wf-ink-3); + --ink-4: var(--wf-ink-4); + --paper: var(--wf-paper); + --paper-2: var(--wf-paper-2); + --accent: var(--wf-accent); + --accent-soft: var(--wf-accent-soft); + --yellow: var(--wf-yellow); + --hand: var(--wf-hand); + --hand-tight: var(--wf-hand-tight); + --mono: var(--wf-mono); + + margin: 0; + padding: 0; + background: #f0eee9; + font-family: 'Kalam', cursive; + box-sizing: border-box; + height: 100%; + width: 100%; +} + +/* ── Wireframe base ── */ +.wf-preview-root .wf { + font-family: var(--wf-hand-tight); + color: var(--wf-ink); + background: var(--wf-paper); + line-height: 1.35; + box-sizing: border-box; +} +.wf-preview-root .wf *, +.wf-preview-root .wf *::before, +.wf-preview-root .wf *::after { + box-sizing: border-box; +} + +/* Boxes */ +.wf-preview-root .wf .box { + border: 1.5px solid var(--wf-ink); + border-radius: 2px; + position: relative; +} +.wf-preview-root .wf .box-dashed { border-style: dashed; } +.wf-preview-root .wf .box-soft { border-color: var(--wf-ink-3); } +.wf-preview-root .wf .box-accent { border-color: var(--wf-accent); } + +/* Sketchy wavy underline */ +.wf-preview-root .wf .u-sketch { + background-image: url("data:image/svg+xml;utf8,"); + background-repeat: repeat-x; + background-position: 0 100%; + background-size: 120px 6px; + padding-bottom: 6px; +} + +/* Text utilities */ +.wf-preview-root .wf .hand { font-family: var(--wf-hand); } +.wf-preview-root .wf .hand-tight { font-family: var(--wf-hand-tight); } +.wf-preview-root .wf .mono { font-family: var(--wf-mono); } +.wf-preview-root .wf .muted { color: var(--wf-ink-3); } +.wf-preview-root .wf .fade { color: var(--wf-ink-4); } +.wf-preview-root .wf .accent { color: var(--wf-accent); } +.wf-preview-root .wf .hi { + background: var(--wf-yellow); + padding: 0 4px; + box-decoration-break: clone; + -webkit-box-decoration-break: clone; +} + +/* Placeholder image */ +.wf-preview-root .wf .ph { + background: + repeating-linear-gradient(-45deg, + transparent 0, transparent 10px, + rgba(0,0,0,0.07) 10px, rgba(0,0,0,0.07) 11px); + border: 1.5px solid var(--wf-ink-3); + display: flex; align-items: center; justify-content: center; + color: var(--wf-ink-3); + font-family: var(--wf-mono); + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.08em; + text-align: center; + padding: 8px; +} + +/* Sketchy button */ +.wf-preview-root .wf .btn { + display: inline-flex; align-items: center; gap: 8px; + border: 1.5px solid var(--wf-ink); + padding: 10px 18px; + font-family: var(--wf-hand-tight); + font-size: 15px; + background: var(--wf-paper); + color: var(--wf-ink); + box-shadow: 3px 3px 0 var(--wf-ink); + text-decoration: none; + cursor: pointer; +} +.wf-preview-root .wf .btn-accent { + background: var(--wf-accent); + color: white; + border-color: var(--wf-ink); +} +.wf-preview-root .wf .btn-ghost { + box-shadow: none; + background: transparent; + border-style: dashed; +} + +/* Chip */ +.wf-preview-root .wf .chip { + display: inline-block; + border: 1.2px solid var(--wf-ink-2); + padding: 2px 8px; + font-size: 11px; + font-family: var(--wf-mono); + border-radius: 999px; + background: var(--wf-paper); +} +.wf-preview-root .wf .chip-accent { + border-color: var(--wf-accent); + color: var(--wf-accent); +} + +.wf-preview-root .wf .arrow { + font-family: var(--wf-hand); + color: var(--wf-accent); +} + +/* Annotation callout */ +.wf-preview-root .wf .note { + position: absolute; + font-family: var(--wf-hand); + color: var(--wf-accent); + font-size: 14px; + line-height: 1.2; + max-width: 180px; + pointer-events: none; +} +.wf-preview-root .wf .note svg { + position: absolute; + overflow: visible; +} + +/* Density: roomy mode */ +.wf-preview-root.roomy .wf .page { + padding: 48px 64px !important; + line-height: 1.5; +} +.wf-preview-root.roomy .wf h1 { margin-bottom: 24px !important; } +.wf-preview-root.roomy .wf-nav { margin-bottom: 40px !important; } + +/* Notes hidden */ +.wf-preview-root.notes-hidden .wf .note { display: none !important; } + +/* Fake text lines */ +.wf-preview-root .wf .line { + height: 7px; + background: var(--wf-ink-4); + border-radius: 2px; + opacity: 0.35; + margin: 6px 0; +} +.wf-preview-root .wf .line.w-90 { width: 90%; } +.wf-preview-root .wf .line.w-80 { width: 80%; } +.wf-preview-root .wf .line.w-70 { width: 70%; } +.wf-preview-root .wf .line.w-60 { width: 60%; } +.wf-preview-root .wf .line.w-50 { width: 50%; } +.wf-preview-root .wf .line.w-40 { width: 40%; } + +/* Scribbled divider */ +.wf-preview-root .wf hr.scribble { + border: none; + height: 4px; + background-image: url("data:image/svg+xml;utf8,"); + background-repeat: repeat-x; + background-size: 200px 4px; + margin: 16px 0; +} + +/* Fake browser artboard */ +.wf-preview-root .wf-browser { + background: var(--wf-paper); + width: 100%; height: 100%; + display: flex; flex-direction: column; + overflow: hidden; +} +.wf-preview-root .wf-browser .chrome { + display: flex; align-items: center; gap: 10px; + padding: 10px 14px; + border-bottom: 1.5px solid var(--wf-ink); + background: var(--wf-paper-2); + font-family: var(--wf-mono); + font-size: 11px; + color: var(--wf-ink-3); + flex-shrink: 0; +} +.wf-preview-root .wf-browser .chrome .dot { + width: 10px; height: 10px; + border-radius: 50%; + border: 1.2px solid var(--wf-ink-3); +} +.wf-preview-root .wf-browser .chrome .url { + flex: 1; + border: 1.2px solid var(--wf-ink-3); + border-radius: 12px; + padding: 2px 10px; + background: var(--wf-paper); +} +.wf-preview-root .wf-browser .page { + flex: 1; + overflow: auto; + padding: 32px 40px; +} + +/* Nav */ +.wf-preview-root .wf-nav { + display: flex; align-items: center; justify-content: space-between; + padding-bottom: 18px; + border-bottom: 1.2px dashed var(--wf-ink-3); + margin-bottom: 28px; +} +.wf-preview-root .wf-nav .logo { + font-weight: 700; font-size: 18px; + display: flex; align-items: center; gap: 8px; +} +.wf-preview-root .wf-nav .logo-mark { + width: 28px; height: 28px; + border: 1.5px solid var(--wf-ink); + display: flex; align-items: center; justify-content: center; + font-family: var(--wf-mono); font-size: 13px; font-weight: 700; + background: var(--wf-paper); +} +.wf-preview-root .wf-nav .links { + display: flex; gap: 22px; + font-size: 14px; +} +.wf-preview-root .wf-nav .links span { cursor: pointer; } + +/* Footer */ +.wf-preview-root .wf-foot { + margin-top: 48px; padding-top: 20px; + border-top: 1.2px dashed var(--wf-ink-3); + display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; + gap: 24px; + font-size: 13px; +} +.wf-preview-root .wf-foot h5 { + font-size: 11px; font-family: var(--wf-mono); + text-transform: uppercase; letter-spacing: 0.08em; + color: var(--wf-ink-3); margin: 0 0 10px; font-weight: 500; +} +.wf-preview-root .wf-foot ul { list-style: none; padding: 0; margin: 0; } +.wf-preview-root .wf-foot li { margin-bottom: 6px; } + +/* ── Preview chrome (toolbar, badge, tweaks) ── */ +.wf-preview-root .preview-toolbar { + position: fixed; top: 16px; left: 50%; transform: translateX(-50%); + z-index: 1000; + display: flex; gap: 6px; + background: #fbfaf6; + border: 1.5px solid #1a1a1a; + box-shadow: 3px 3px 0 #1a1a1a; + padding: 6px; + font-family: 'Architects Daughter', cursive; +} +.wf-preview-root .preview-toolbar button { + font-family: 'Architects Daughter', cursive; + font-size: 14px; + background: transparent; + border: none; + padding: 8px 14px; + cursor: pointer; + color: #333; +} +.wf-preview-root .preview-toolbar button:hover { background: rgba(0,0,0,0.05); } +.wf-preview-root .preview-toolbar button.active { + background: #1a1a1a; + color: #fbfaf6; +} +.wf-preview-root .preview-toolbar .sep { + width: 1px; background: rgba(0,0,0,0.15); margin: 4px 2px; +} +.wf-preview-root .preview-toolbar .mono-label { + font-family: 'JetBrains Mono', monospace; + font-size: 10px; + color: #666; + align-self: center; + padding: 0 8px; + letter-spacing: 0.1em; +} + +/* Single view */ +.wf-preview-root .single-stage { + min-height: 100vh; + padding: 90px 40px 40px; + display: flex; justify-content: center; +} +.wf-preview-root .single-stage .frame { + width: 1280px; + max-width: 100%; + height: calc(100vh - 130px); + min-height: 800px; + box-shadow: 0 2px 6px rgba(0,0,0,0.1), 0 10px 40px rgba(0,0,0,0.12); + border: 1.5px solid #1a1a1a; + overflow: hidden; +} + +/* Title badge */ +.wf-preview-root .title-badge { + position: fixed; bottom: 20px; left: 20px; + z-index: 1000; + background: #fef4a8; + border: 1.5px solid #1a1a1a; + box-shadow: 3px 3px 0 #1a1a1a; + padding: 10px 14px; + font-family: 'Kalam', cursive; + font-size: 13px; + max-width: 280px; + line-height: 1.3; +} +.wf-preview-root .title-badge b { + font-size: 15px; display: block; margin-bottom: 2px; +} +.wf-preview-root .title-badge .muted-label { + color: #666; + font-family: 'JetBrains Mono', monospace; + font-size: 10px; + letter-spacing: 0.1em; +} + +/* Tweaks panel */ +.wf-preview-root .tweaks-panel { + position: fixed; bottom: 20px; right: 20px; + z-index: 1000; + background: #fbfaf6; + border: 1.5px solid #1a1a1a; + box-shadow: 3px 3px 0 #1a1a1a; + padding: 14px 16px; + font-family: 'Architects Daughter', cursive; + font-size: 13px; + width: 220px; +} +.wf-preview-root .tweaks-panel h5 { + margin: 0 0 10px; + font-family: 'JetBrains Mono', monospace; + font-size: 10px; + letter-spacing: 0.14em; + color: #666; + font-weight: 500; +} +.wf-preview-root .tweaks-panel .tweak-row { margin-bottom: 12px; } +.wf-preview-root .tweaks-panel label { + display: block; margin-bottom: 4px; font-size: 11px; color: #666; +} +.wf-preview-root .tweaks-panel .seg { + display: flex; + border: 1.2px solid #1a1a1a; +} +.wf-preview-root .tweaks-panel .seg button { + flex: 1; + font-family: 'Architects Daughter', cursive; + font-size: 12px; + background: transparent; + border: none; + padding: 6px; + cursor: pointer; +} +.wf-preview-root .tweaks-panel .seg button.on { + background: #1a1a1a; + color: #fbfaf6; +} +.wf-preview-root .tweaks-panel .swatches { + display: flex; gap: 6px; +} +.wf-preview-root .tweaks-panel .swatches button { + width: 28px; height: 28px; padding: 0; + border: 1.2px solid #1a1a1a; + cursor: pointer; +} +.wf-preview-root .tweaks-panel .swatches button.on { + outline: 2px solid #1a1a1a; + outline-offset: 2px; +} + +/* Accessibility focus-visible styles for preview toolbar and tweaks panel */ +.wf-preview-root .preview-toolbar button:focus-visible, +.wf-preview-root .tweaks-panel .seg button:focus-visible, +.wf-preview-root .tweaks-panel .swatches button:focus-visible { + outline: 2px solid #333; + outline-offset: 2px; +} diff --git a/src/components/OpenEmbeddingsConceptDemo/index.js b/src/components/OpenEmbeddingsConceptDemo/index.js new file mode 100644 index 0000000..4b22f64 --- /dev/null +++ b/src/components/OpenEmbeddingsConceptDemo/index.js @@ -0,0 +1,374 @@ +import React, {useState} from 'react'; +import styles from './styles.module.css'; + +const conceptTopics = [ + { + slug: 'turkey', + title: 'Turkey', + languages: '61 languages', + angle: 'geopolitics and state identity', + summary: 'Rare concepts inside one multilingual topic cluster become searchable English cards.', + cards: [ + { + label: 'Istanbul as a global city', + text: 'Several leads emphasize Istanbul as a superlative city spanning continents, not just as a former capital or largest city.', + languages: 'zh / ka / tr', + sources: [ + ['zh', 'https://zh.wikipedia.org/wiki/%E5%9C%9F%E8%80%B3%E5%85%B6'], + ['tr', 'https://tr.wikipedia.org/wiki/T%C3%BCrkiye'], + ], + }, + { + label: 'Ottoman succession', + text: 'Some leads frame modern Turkey through the collapse of the Ottoman Empire and the founding of the republic in 1923.', + languages: 'bg / cy / pl / zh', + sources: [ + ['pl', 'https://pl.wikipedia.org/wiki/Turcja'], + ['zh', 'https://zh.wikipedia.org/wiki/%E5%9C%9F%E8%80%B3%E5%85%B6'], + ], + }, + { + label: 'Kurdish population framing', + text: 'Some language leads include ethnic and minority-population framing that does not appear uniformly.', + languages: 'ar / da / ko / mk', + sources: [ + ['ar', 'https://ar.wikipedia.org/wiki/%D8%AA%D8%B1%D9%83%D9%8A%D8%A7'], + ['ko', 'https://ko.wikipedia.org/wiki/%ED%8A%80%EB%A5%B4%ED%82%A4%EC%98%88'], + ], + }, + ], + }, + { + slug: 'europe', + title: 'Europe', + languages: '61 languages', + angle: 'definition and boundary ambiguity', + summary: 'The best conceptual ambiguity example: geography, culture, politics, boundaries, and islands all compete.', + cards: [ + { + label: 'Historical-cultural construct', + text: 'Italian frames Europe as commonly treated as a continent because of historical-cultural and geopolitical factors.', + languages: 'it', + sources: [['it', 'https://it.wikipedia.org/wiki/Europa']], + }, + { + label: 'Arbitrary category', + text: 'Romanian explicitly says Europe can be cultural, political, or geographic.', + languages: 'ro', + sources: [['ro', 'https://ro.wikipedia.org/wiki/Europa']], + }, + { + label: 'Islands and archipelagos', + text: 'Catalan expands the concept by naming nearby Atlantic and Mediterranean islands.', + languages: 'ca', + sources: [['ca', 'https://ca.wikipedia.org/wiki/Europa']], + }, + ], + }, + { + slug: 'jesus', + title: 'Jesus', + languages: '61 languages', + angle: 'historical figure vs theological role', + summary: 'A useful framing topic because leads vary between historical preacher, Christian messiah, Islamic prophet, and theological incarnation.', + cards: [ + { + label: 'Historical preacher and Roman Judea', + text: 'English emphasizes Jesus as a first-century Jewish preacher in the Roman province of Judaea before moving to Christian theology.', + languages: 'en', + sources: [['en', 'https://en.wikipedia.org/wiki/Jesus']], + }, + { + label: 'Prophet in Islam', + text: 'Polish explicitly mentions Jesus as an important prophet in Islam alongside Christian messiah language.', + languages: 'pl', + sources: [['pl', 'https://pl.wikipedia.org/wiki/Jezus_Chrystus']], + }, + { + label: 'Different religious readings', + text: 'Chinese foregrounds Jesus as Christian Son, Islamic prophet, and ordinary historical person in Judaism.', + languages: 'zh', + sources: [['zh', 'https://zh.wikipedia.org/wiki/%E8%80%B6%E7%A8%A3']], + }, + ], + }, + { + slug: 'united_states', + title: 'United States', + languages: '61 languages', + angle: 'federal structure and territorial framing', + summary: 'The country page varies between constitutional structure, territory, population, immigration, and naming conventions.', + cards: [ + { + label: 'Indigenous and external territories', + text: 'Chinese includes 326 Indian reservations and claims to offshore territories and outlying islands.', + languages: 'zh', + sources: [['zh', 'https://zh.wikipedia.org/wiki/%E7%BE%8E%E5%9B%BD']], + }, + { + label: 'Transcontinental framing', + text: 'French emphasizes the United States as a transcontinental state, not simply a North American country.', + languages: 'fr', + sources: [['fr', 'https://fr.wikipedia.org/wiki/%C3%89tats-Unis']], + }, + { + label: 'Democratic federal republic', + text: 'German starts with constitutional democratic-federal structure and includes territories beyond the 50 states.', + languages: 'de', + sources: [['de', 'https://de.wikipedia.org/wiki/Vereinigte_Staaten']], + }, + ], + }, + { + slug: 'earth', + title: 'Earth', + languages: '10-language pass', + angle: 'planet facts vs human world', + summary: 'Earth leads are mostly aligned, but they differ in whether they foreground life, water, density, age, or orbital mechanics.', + cards: [ + { + label: 'Ocean world', + text: 'English foregrounds Earth as an ocean world and quantifies ocean coverage at 70.8% of the crust.', + languages: 'en', + sources: [['en', 'https://en.wikipedia.org/wiki/Earth']], + }, + { + label: 'Orbital mechanics', + text: 'Chinese includes distance, mass, radius, density, rotation, revolution, axial tilt, Moon, Trojans, and quasi-satellites.', + languages: 'zh', + sources: [['zh', 'https://zh.wikipedia.org/wiki/%E5%9C%B0%E7%90%83']], + }, + { + label: 'No other Solar System life', + text: 'German says no other planet in the Solar System has been found to host life.', + languages: 'de', + sources: [['de', 'https://de.wikipedia.org/wiki/Erde']], + }, + ], + }, + { + slug: 'cat', + title: 'Cat', + languages: '10-language pass', + angle: 'domestication, taxonomy, and risk', + summary: 'A lighter example: pet, predator, taxonomy, pest control, domestication history, and ecological harm.', + cards: [ + { + label: 'Pest control', + text: 'Arabic explains domestication through controlling crop-damaging rodents and venomous reptiles.', + languages: 'ar', + sources: [['ar', 'https://ar.wikipedia.org/wiki/%D9%82%D8%B7']], + }, + { + label: 'Invasive species caveat', + text: 'Polish mentions IUCN/SSC invasive-species status in many areas.', + languages: 'pl', + sources: [['pl', 'https://pl.wikipedia.org/wiki/Kot_domowy']], + }, + { + label: 'Fertile Crescent origin', + text: 'Chinese ties cat keeping to the Fertile Crescent and ancient Egyptian grain protection.', + languages: 'zh', + sources: [['zh', 'https://zh.wikipedia.org/wiki/%E7%8C%AB']], + }, + ], + }, + { + slug: 'dog', + title: 'Dog', + languages: '10-language pass', + angle: 'companionship, work, and taxonomy', + summary: 'Dog pages split between companion framing, wolf taxonomy, domestication timing, working roles, and environmental caveats.', + cards: [ + { + label: 'Best friend and work roles', + text: 'Arabic uses the “best friend of humans” framing and lists hunting, field, shepherd, guard, and police dogs.', + languages: 'ar', + sources: [['ar', 'https://ar.wikipedia.org/wiki/%D9%83%D9%84%D8%A8']], + }, + { + label: 'Wolf boundary problem', + text: 'German says dog and wolf are not separated by a species barrier and can produce fertile offspring.', + languages: 'de', + sources: [['de', 'https://de.wikipedia.org/wiki/Haushund']], + }, + { + label: 'Environmental impact', + text: 'Chinese notes feral dogs may affect the environment and other organisms.', + languages: 'zh', + sources: [['zh', 'https://zh.wikipedia.org/wiki/%E7%8A%AC']], + }, + ], + }, + { + slug: 'christianity', + title: 'Christianity', + languages: '10-language pass', + angle: 'doctrine, salvation, and institutions', + summary: 'Christianity leads differ in how much they emphasize doctrine, global scale, salvation, Trinity, or historical origin.', + cards: [ + { + label: 'Spiritual and social teaching', + text: 'Arabic emphasizes New Testament spiritual, social, and ethical teaching, miracles, original sin, crucifixion, resurrection, and mediation.', + languages: 'ar', + sources: [['ar', 'https://ar.wikipedia.org/wiki/%D8%A7%D9%84%D9%85%D8%B3%D9%8A%D8%AD%D9%8A%D8%A9']], + }, + { + label: 'Trinity foregrounded', + text: 'Japanese explicitly foregrounds Father, Son, and Holy Spirit as the one God for most Christian denominations.', + languages: 'ja', + sources: [['ja', 'https://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%AA%E3%82%B9%E3%83%88%E6%95%99']], + }, + { + label: 'Judea under Rome', + text: 'Polish places the origin in first-century Western Asia, Judea under the Roman Empire.', + languages: 'pl', + sources: [['pl', 'https://pl.wikipedia.org/wiki/Chrze%C5%9Bcija%C5%84stwo']], + }, + ], + }, + { + slug: 'islam', + title: 'Islam', + languages: '10-language pass', + angle: 'scripture, revelation, and world scale', + summary: 'Islam leads vary between shahada, Quran, Muhammad, revelation, Gabriel, and population scale.', + cards: [ + { + label: 'Revelation over 23 years', + text: 'Arabic says the Quran was revealed to Muhammad over 23 years in Mecca and Medina.', + languages: 'ar', + sources: [['ar', 'https://ar.wikipedia.org/wiki/%D8%A7%D9%84%D8%A5%D8%B3%D9%84%D8%A7%D9%85']], + }, + { + label: 'Absolute monotheism', + text: 'French foregrounds the dogma of absolute monotheism and the Quran as God’s word revealed in seventh-century Arabia.', + languages: 'fr', + sources: [['fr', 'https://fr.wikipedia.org/wiki/Islam']], + }, + { + label: 'Gabriel and final scripture', + text: 'Polish names Gabriel and frames the Quran as the holy book revealed to Muhammad from 610 to 632.', + languages: 'pl', + sources: [['pl', 'https://pl.wikipedia.org/wiki/Islam']], + }, + ], + }, + { + slug: 'world_war_ii', + title: 'World War II', + languages: '10-language pass', + angle: 'national framing and scale', + summary: 'A strong national-framing topic: languages choose different war-scale facts to foreground.', + cards: [ + { + label: 'Named participants', + text: 'Japanese names the central Axis and Allied countries directly in the lead.', + languages: 'ja', + sources: [['ja', 'https://ja.wikipedia.org/wiki/%E7%AC%AC%E4%BA%8C%E6%AC%A1%E4%B8%96%E7%95%8C%E5%A4%A7%E6%88%A6']], + }, + { + label: 'Old World theater', + text: 'Polish emphasizes Eurasia, Africa, oceans, Oceania, and episodes in the Americas.', + languages: 'pl', + sources: [['pl', 'https://pl.wikipedia.org/wiki/II_wojna_%C5%9Bwiatowa']], + }, + { + label: 'Nuclear weapons and civilians', + text: 'Russian explicitly foregrounds nuclear weapons and majority-civilian deaths.', + languages: 'ru', + sources: [['ru', 'https://ru.wikipedia.org/wiki/%D0%92%D1%82%D0%BE%D1%80%D0%B0%D1%8F_%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%8F_%D0%B2%D0%BE%D0%B9%D0%BD%D0%B0']], + }, + ], + }, +]; + +function SourceLinks({sources}) { + if (!sources?.length) { + return null; + } + return ( +
+ {sources.map(([label, href]) => ( + + {label} + + ))} +
+ ); +} + +function TopicSelect({selected, onSelect}) { + return ( + + ); +} + +export default function OpenEmbeddingsConceptDemo() { + const [selected, setSelected] = useState(conceptTopics[0].slug); + const topic = conceptTopics.find((item) => item.slug === selected) ?? conceptTopics[0]; + + return ( +
+
+
+

Interactive research note

+

Language-specific concept cards

+

+ Select any processed topic to see the kind of English-normalized concept layer a + publisher could expose alongside Open Embeddings. +

+
+
+ 304 rows + 8 spaces + 10 topics +
+
+ +
+ +
+ {conceptTopics.slice(0, 5).map((item) => ( + + ))} +
+
+ +
+
+ {topic.languages} +

{topic.title}

+

{topic.summary}

+
+ {topic.angle} +
+ +
+ {topic.cards.map((card) => ( +
+ {card.languages} +
{card.label}
+

{card.text}

+ +
+ ))} +
+
+ ); +} diff --git a/src/components/OpenEmbeddingsConceptDemo/styles.module.css b/src/components/OpenEmbeddingsConceptDemo/styles.module.css new file mode 100644 index 0000000..94cfd78 --- /dev/null +++ b/src/components/OpenEmbeddingsConceptDemo/styles.module.css @@ -0,0 +1,336 @@ +.demo { + position: relative; + overflow: hidden; + /* FIXME: Dark theme currently appears cut off after roughly one page in the blog layout. + Re-check parent article/container background and height handling before publishing. */ + margin: 2rem 0; + padding: clamp(1rem, 2.5vw, 1.6rem); + border: 1px solid rgba(15, 23, 42, 0.12); + border-radius: 28px; + background: + radial-gradient(circle at 10% 0%, rgba(56, 189, 248, 0.22), transparent 28rem), + radial-gradient(circle at 100% 15%, rgba(34, 197, 94, 0.16), transparent 22rem), + linear-gradient(135deg, rgba(255, 255, 255, 0.96), rgba(241, 245, 249, 0.92)); + box-shadow: 0 24px 70px rgba(15, 23, 42, 0.12); +} + +[data-theme='dark'] .demo { + border-color: rgba(148, 163, 184, 0.18); + background: + radial-gradient(circle at 10% 0%, rgba(14, 165, 233, 0.22), transparent 28rem), + radial-gradient(circle at 100% 15%, rgba(34, 197, 94, 0.14), transparent 22rem), + linear-gradient(135deg, rgba(15, 23, 42, 0.96), rgba(2, 6, 23, 0.92)); + box-shadow: 0 24px 70px rgba(0, 0, 0, 0.28); +} + +.header { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 1.25rem; + align-items: start; + margin-bottom: 1.25rem; +} + +.kicker { + margin: 0 0 0.35rem; + color: #0f766e; + font-size: 0.78rem; + font-weight: 800; + letter-spacing: 0.12em; + text-transform: uppercase; +} + +[data-theme='dark'] .kicker { + color: #67e8f9; +} + +.header h3 { + margin: 0; + font-size: clamp(1.45rem, 3vw, 2rem); + line-height: 1.05; +} + +.header p { + max-width: 48rem; + margin: 0.65rem 0 0; + color: var(--text-muted); +} + +.stats { + display: grid; + grid-template-columns: repeat(3, minmax(4.5rem, 1fr)); + gap: 0.45rem; + min-width: min(100%, 17rem); +} + +.stats span { + display: grid; + gap: 0.1rem; + padding: 0.7rem; + border: 1px solid rgba(15, 23, 42, 0.1); + border-radius: 16px; + background: rgba(255, 255, 255, 0.72); + color: var(--text-muted); + font-size: 0.76rem; + text-align: center; +} + +[data-theme='dark'] .stats span { + border-color: rgba(148, 163, 184, 0.18); + background: rgba(15, 23, 42, 0.62); +} + +.stats strong { + color: var(--text-main); + font-size: 1.25rem; + line-height: 1; +} + +.controls { + display: grid; + grid-template-columns: minmax(12rem, 18rem) minmax(0, 1fr); + gap: 0.75rem; + align-items: stretch; + margin-bottom: 1rem; +} + +.selectWrap { + display: grid; + gap: 0.28rem; + padding: 0.35rem 0.55rem; + border: 1px solid rgba(15, 23, 42, 0.08); + border-radius: 18px; + background: rgba(255, 255, 255, 0.64); +} + +[data-theme='dark'] .selectWrap { + border-color: rgba(148, 163, 184, 0.14); + background: rgba(15, 23, 42, 0.5); +} + +.selectWrap span { + color: var(--text-muted); + font-size: 0.7rem; + font-weight: 800; + letter-spacing: 0.1em; + text-transform: uppercase; +} + +.selectWrap select { + width: 100%; + border: 0; + background: transparent; + color: var(--text-main); + font: inherit; + font-weight: 800; + outline: none; +} + +.tabs { + display: flex; + gap: 0.5rem; + flex-wrap: wrap; + padding: 0.35rem; + border: 1px solid rgba(15, 23, 42, 0.08); + border-radius: 999px; + background: rgba(255, 255, 255, 0.58); +} + +[data-theme='dark'] .tabs { + border-color: rgba(148, 163, 184, 0.14); + background: rgba(15, 23, 42, 0.5); +} + +.tab { + border: 0; + background: transparent; + border-radius: 999px; + padding: 0.55rem 0.9rem; + color: var(--text-muted); + cursor: pointer; + font: inherit; + font-weight: 700; + transition: background 160ms ease, color 160ms ease, transform 160ms ease; +} + +.tab:hover { + color: var(--text-main); + transform: translateY(-1px); +} + +.tabActive { + background: #0f172a; + color: white; + box-shadow: 0 10px 22px rgba(15, 23, 42, 0.18); +} + +[data-theme='dark'] .tabActive { + background: #22c55e; + color: #052e16; +} + +.topicIntro { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 1rem; + align-items: center; + margin-bottom: 1rem; + padding: 1rem; + border: 1px solid rgba(15, 23, 42, 0.09); + border-radius: 20px; + background: rgba(255, 255, 255, 0.64); +} + +[data-theme='dark'] .topicIntro { + border-color: rgba(148, 163, 184, 0.16); + background: rgba(15, 23, 42, 0.54); +} + +.eyebrow { + display: inline-block; + margin-bottom: 0.35rem; + color: #0369a1; + font-size: 0.74rem; + font-weight: 800; + letter-spacing: 0.1em; + text-transform: uppercase; +} + +[data-theme='dark'] .eyebrow { + color: #7dd3fc; +} + +.topicIntro h4 { + margin: 0; + font-size: 1.35rem; +} + +.topicIntro p { + margin: 0.4rem 0 0; + color: var(--text-muted); +} + +.angle { + justify-self: end; + max-width: 13rem; + padding: 0.6rem 0.75rem; + border-radius: 999px; + background: rgba(15, 23, 42, 0.08); + color: #334155; + font-size: 0.82rem; + font-weight: 800; + text-align: center; +} + +[data-theme='dark'] .angle { + background: rgba(148, 163, 184, 0.12); + color: #cbd5e1; +} + +.cards { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 0.85rem; +} + +.card { + min-height: 11rem; + padding: 1rem; + border: 1px solid rgba(15, 23, 42, 0.1); + border-radius: 20px; + background: rgba(255, 255, 255, 0.78); + box-shadow: 0 12px 30px rgba(15, 23, 42, 0.08); +} + +[data-theme='dark'] .card { + border-color: rgba(148, 163, 184, 0.14); + background: rgba(2, 6, 23, 0.46); + box-shadow: none; +} + +.card span { + display: inline-flex; + margin-bottom: 0.65rem; + padding: 0.22rem 0.5rem; + border-radius: 999px; + background: rgba(14, 165, 233, 0.12); + color: #0369a1; + font-family: var(--font-mono); + font-size: 0.72rem; + font-weight: 800; +} + +[data-theme='dark'] .card span { + background: rgba(14, 165, 233, 0.18); + color: #7dd3fc; +} + +.card h5 { + margin: 0 0 0.45rem; + font-size: 1rem; + line-height: 1.2; +} + +.card p { + margin: 0; + color: var(--text-muted); + font-size: 0.92rem; +} + +.sources { + display: flex; + flex-wrap: wrap; + gap: 0.4rem; + margin-top: 0.9rem; +} + +.sources a { + display: inline-flex; + align-items: center; + border: 1px solid rgba(14, 165, 233, 0.24); + border-radius: 999px; + padding: 0.2rem 0.48rem; + background: rgba(14, 165, 233, 0.08); + color: #0369a1; + font-family: var(--font-mono); + font-size: 0.72rem; + font-weight: 800; + text-decoration: none; +} + +.sources a:hover { + background: rgba(14, 165, 233, 0.16); + text-decoration: none; +} + +[data-theme='dark'] .sources a { + border-color: rgba(125, 211, 252, 0.24); + background: rgba(14, 165, 233, 0.12); + color: #7dd3fc; +} + +@media (max-width: 760px) { + .header, + .topicIntro, + .controls { + grid-template-columns: 1fr; + } + + .stats, + .cards { + grid-template-columns: 1fr; + } + + .tabs { + border-radius: 20px; + } + + .tab { + flex: 1 1 calc(50% - 0.5rem); + } + + .angle { + justify-self: start; + max-width: none; + } +} diff --git a/src/config/areas.json b/src/config/areas.json new file mode 100644 index 0000000..bee25ad --- /dev/null +++ b/src/config/areas.json @@ -0,0 +1,6 @@ +{ + "blog-self-hosted-iot": "self-hosted-iot", + "blog-applied-home-ml-iot": "home-ml-iot", + "blog-applied-ai-engineering": "applied-ai", + "blog-frontier-research": "frontier" +} diff --git a/src/config/homepage.js b/src/config/homepage.js index 3bd9572..bb65e4a 100644 --- a/src/config/homepage.js +++ b/src/config/homepage.js @@ -1,10 +1,24 @@ +import areaLabels from './areas.json'; + const homepageConfig = { missionStatement: "Advancing Technology for Humans and Bots with Privacy in mind.", + contactInfo: { email: "inquiries@5l-labs.com", linkedin: "https://www.linkedin.com/company/5l-labs/", twitter: "https://twitter.com/5l_labs", }, + + // Canonical list derived from src/config/areas.json (shared with generate-latest-post.js) + areas: Object.values(areaLabels), + + consulting: { + availability: 'Q3 2026', + blurb: 'We take on a small number of engagements each quarter.', + services: 'Private ML systems, on-device inference, and IoT architecture audits.', + inquireUrl: '/inquiry', + }, + researchAreas: [ { title: "Private AI/ML", @@ -17,6 +31,7 @@ const homepageConfig = { link: "/self-hosted-iot", }, ], + products: [ { title: "Recruiter Rankings (Preview)", diff --git a/src/generated/all-posts.json b/src/generated/all-posts.json new file mode 100644 index 0000000..1f21278 --- /dev/null +++ b/src/generated/all-posts.json @@ -0,0 +1,209 @@ +[ + { + "date": "2026-06-12T00:00:00.000Z", + "dateLabel": "2026-06-12", + "title": "Side-Quest - What Multilingual Wikipedia Reveals About Open Embeddings", + "area": "applied-ai", + "type": "writing", + "url": "/applied-ai-engineering/open-embeddings-multilingual-wikipedia", + "excerpt": "A practical Open Embedding Space research demo using multilingual Wikipedia leads, shared latent spaces, and language-specific concept cards." + }, + { + "date": "2026-05-29T00:00:00.000Z", + "dateLabel": "2026-05-29", + "title": "Anatomy of an AI Agent Skill: The Structure Behind 11 Custom Modules", + "area": "applied-ai", + "type": "writing", + "url": "/applied-ai-engineering/anatomy-of-an-ai-agent-skill", + "excerpt": "A field guide to the structure of AI agent skills — the recurring sections, three archetypes, and design philosophy behind 11 custom skill modules that power daily operations." + }, + { + "date": "2026-05-24T00:00:00.000Z", + "dateLabel": "2026-05-24", + "title": "A Shared Latent Space for (Text) Embeddings?", + "area": "frontier", + "type": "writing", + "url": "/frontier-research/shared-latent-embeddings", + "excerpt": "Short notes from current work on shared latent spaces and Matryoshka-style embedding layers." + }, + { + "date": "2026-04-01T00:00:00.000Z", + "dateLabel": "2026-04-01", + "title": "5L Labs Is Working on Recursive Self-Improvement", + "area": "frontier", + "type": "writing", + "url": "/frontier-research/recursive-self-improvement", + "excerpt": "5L Labs is pursuing recursive self-improvement with a fundamentally new technique derived from genetic algorithms." + }, + { + "date": "2026-02-23T00:00:00.000Z", + "dateLabel": "2026-02-23", + "title": "Learning about learning?", + "area": "frontier", + "type": "writing", + "url": "/frontier-research/learning-again", + "excerpt": "Exploring the limits of Transformer architectures, the Platonic Representation Hypothesis, and the role of curiosity-driven learning in the next generation of AI." + }, + { + "date": "2026-02-18T00:00:00.000Z", + "dateLabel": "2026-02-18", + "title": "GarageCam Lives! ExecuTorch on RPi 5", + "area": "home-ml-iot", + "type": "writing", + "url": "/applied-home-ml-iot/executorch-bootstrap", + "excerpt": "A deep dive into deploying a private ML pipeline for GarageCam using ExecuTorch on Raspberry Pi 5, featuring real-time inference and MQTT integration." + }, + { + "date": "2025-12-10T00:00:00.000Z", + "dateLabel": "2025-12-10", + "title": "Time Decay of Information", + "area": "frontier", + "type": "writing", + "url": "/frontier-research/information-aging-out", + "excerpt": "Exploring the \"Time Decay\" of information in AI models—how embeddings and weights handle the aging of explicit, implicit, and undated knowledge." + }, + { + "date": "2025-12-01T00:00:00.000Z", + "dateLabel": "2025-12-01", + "title": "NetworkManager and VPN Tunneling", + "area": "self-hosted-iot", + "type": "writing", + "url": "/self-hosted-iot/network-manager-forced-updates", + "excerpt": "A technical guide to configuring NetworkManager, static IPs, and WireGuard VPN tunneling on Debian Bookworm/Trixie for private home networking." + }, + { + "date": "2025-11-16T00:00:00.000Z", + "dateLabel": "2025-11-16", + "title": "Thoughts on Jarvis 2.0", + "area": "home-ml-iot", + "type": "writing", + "url": "/applied-home-ml-iot/jarvis2.0", + "excerpt": "Conceptualizing \"Jarvis 2.0\"—a private, multi-modal AI assistant for the home, utilizing semantic routing and local speech recognition." + }, + { + "date": "2025-10-19T00:00:00.000Z", + "dateLabel": "2025-10-19", + "title": "Overlord - Home Network Kill Switch", + "area": "self-hosted-iot", + "type": "writing", + "url": "/self-hosted-iot/updated-network-controls", + "excerpt": "Updates on the Overlord Network Kill Switch project, supporting Pi-hole v6 and Ubiquiti 9.4.19 for simple, one-tap parental controls in HomeKit." + }, + { + "date": "2025-09-15T00:00:00.000Z", + "dateLabel": "2025-09-15", + "title": "Continued Adventures in CheapML for IoT", + "area": "home-ml-iot", + "type": "writing", + "url": "/applied-home-ml-iot/continued-iot-ml-on-the-cheap", + "excerpt": "A progress update on the GarageCam project, focusing on VLM labeling (Qwen), model conversion (PyTorch to TFLite), and the challenges of running ML on an RPi 4." + }, + { + "date": "2025-06-08T00:00:00.000Z", + "dateLabel": "2025-06-08", + "title": "Adventures in CheapML - GarageCam", + "area": "home-ml-iot", + "type": "writing", + "url": "/applied-home-ml-iot/garage-cam-on-the-cheap", + "excerpt": "A DIY approach to garage door and car presence detection using a Wyze Cam v3, Thingino, and a custom Python script for cheap ML at the edge." + }, + { + "date": "2025-05-17T00:00:00.000Z", + "dateLabel": "2025-05-17", + "title": "Almost Bricking a v3 Wyze Cam (and back again...)", + "area": "self-hosted-iot", + "type": "writing", + "url": "/self-hosted-iot/getting-off-of-wyze-camv3", + "excerpt": "A personal account of flashing the custom Thingino firmware onto a Wyze Cam v3 to achieve local-only control and escape the cloud." + }, + { + "date": "2025-05-03T00:00:00.000Z", + "dateLabel": "2025-05-03", + "title": "Wyzely Saying Goodbye to Wyze!", + "area": "self-hosted-iot", + "type": "writing", + "url": "/self-hosted-iot/getting-off-of-wyze", + "excerpt": "Transitioning away from Wyze's cloud-dependent cameras to a local-only setup using Scrypted, ONVIF, and HomeKit for better privacy and control." + }, + { + "date": "2025-04-19T00:00:00.000Z", + "dateLabel": "2025-04-19", + "title": "Private Agents - Pim Particles (Embeddings)", + "area": "applied-ai", + "type": "writing", + "url": "/applied-ai-engineering/embeddings-pim-particles", + "excerpt": "Exploring the role of embeddings (Pim Particles) in private agent architectures and how federated learning fits into the local-first AI model." + }, + { + "date": "2025-04-12T00:00:00.000Z", + "dateLabel": "2025-04-12", + "title": "Interoperability and Mini Minds", + "area": "home-ml-iot", + "type": "writing", + "url": "/applied-home-ml-iot/interoperability-and-monetization", + "excerpt": "Exploring how multiple small language models (Mini Minds) can collaborate in a local IoT ecosystem, and the potential monetization models for private AI." + }, + { + "date": "2025-04-05T00:00:00.000Z", + "dateLabel": "2025-04-05", + "title": "Private Agents - Pim Particles", + "area": "home-ml-iot", + "type": "writing", + "url": "/applied-home-ml-iot/pim_particles", + "excerpt": "Exploring model shrinkage techniques like LoRA and quantization to run private agents on low-power hardware like Raspberry Pi 4." + }, + { + "date": "2025-03-25T00:00:00.000Z", + "dateLabel": "2025-03-25", + "title": "NVIDIA GTC Recap", + "area": "applied-ai", + "type": "writing", + "url": "/applied-ai-engineering/nvdia-gtc-recap", + "excerpt": "A recap of NVIDIA GTC 2025 focusing on private agency, home-based ML, and the technical ingredients for secure, local AI systems." + }, + { + "date": "2025-03-16T00:00:00.000Z", + "dateLabel": "2025-03-16", + "title": "Off to Nvidia GTC", + "area": "applied-ai", + "type": "writing", + "url": "/applied-ai-engineering/nvdia-gtc", + "excerpt": "Exploring the privacy landscape at NVIDIA GTC 2025, with a focus on Distributed Training and Private LLMs." + }, + { + "date": "2025-02-22T00:00:00.000Z", + "dateLabel": "2025-02-22", + "title": "ai.engineer summit nyc", + "area": "applied-ai", + "type": "writing", + "url": "/applied-ai-engineering/ai-dot-engineer-summit-nyc", + "excerpt": "Key takeaways from the AI Engineering Summit in NYC, focusing on private agents, federated systems, and the Model Context Protocol (MCP)." + }, + { + "date": "2025-02-16T00:00:00.000Z", + "dateLabel": "2025-02-16", + "title": "Entirely Private Machine Learning Home Setup", + "area": "home-ml-iot", + "type": "writing", + "url": "/applied-home-ml-iot/offline-llms", + "excerpt": "A deep dive into setting up a private, local machine learning environment using Podman, Langfuse, Milvus, and more on a Mac M2 Pro." + }, + { + "date": "2025-01-01T00:00:00.000Z", + "dateLabel": "2025-01-01", + "title": "My Private Home", + "area": "self-hosted-iot", + "type": "writing", + "url": "/self-hosted-iot/what-is-my-private-home", + "excerpt": "Defining the architecture of a private home network across multiple locations, focusing on Ubiquity hardware, traffic routing, and privacy-first IoT." + }, + { + "date": "2024-09-30T00:00:00.000Z", + "dateLabel": "2024-09-30", + "title": "School Laptop - When You Don't Have Control", + "area": "self-hosted-iot", + "type": "writing", + "url": "/self-hosted-iot/school-chromebook-bypassing-content-filters", + "excerpt": "Tackling the challenges of managing school-issued Chromebooks that bypass home network controls using TLS 1.2 fallback and enterprise-managed proxies." + } +] \ No newline at end of file diff --git a/src/generated/latest-post.json b/src/generated/latest-post.json index fefe6c5..8781995 100644 --- a/src/generated/latest-post.json +++ b/src/generated/latest-post.json @@ -1,6 +1,6 @@ { - "date": "2026-02-23T00:00:00.000Z", - "title": "Learning about learning?", - "content": "Exploring the limits of Transformer architectures, the Platonic Representation Hypothesis, and the role of curiosity-driven learning in the next generation of AI.", - "url": "/frontier-research/learning-again" + "date": "2026-06-12T00:00:00.000Z", + "title": "Side-Quest - What Multilingual Wikipedia Reveals About Open Embeddings", + "content": "A practical Open Embedding Space research demo using multilingual Wikipedia leads, shared latent spaces, and language-specific concept cards.", + "url": "/applied-ai-engineering/open-embeddings-multilingual-wikipedia" } \ No newline at end of file diff --git a/src/pages/archive.js b/src/pages/archive.js new file mode 100644 index 0000000..9df5779 --- /dev/null +++ b/src/pages/archive.js @@ -0,0 +1,113 @@ +import React, { useState, useEffect, useMemo } from 'react'; +import Link from '@docusaurus/Link'; +import Layout from '@theme/Layout'; +import { useLocation } from '@docusaurus/router'; +import homepageConfig from '../config/homepage'; +import allPosts from '../generated/all-posts.json'; +import styles from './index.module.css'; + +const AREAS = ['all', ...homepageConfig.areas]; + +export default function Archive() { + const location = useLocation(); + const [area, setArea] = useState('all'); + + // Honour deep-links from homepage "load more" (e.g. /archive#home-ml-iot) + useEffect(() => { + const hash = location.hash.replace('#', ''); + if (hash && AREAS.includes(hash)) { + setArea(hash); + } + }, [location.hash]); + + // ⚡ Bolt Perf: Memoize array filtering to prevent redundant O(N) operations on every component re-render unless the area filter changes. + const entries = useMemo(() => area === 'all' ? allPosts : allPosts.filter(p => p.area === area), [area]); + + return ( + +
+ +
+
$ ls -lt writing/
+
+ {allPosts.length} entries across{' '} + {homepageConfig.areas.map((a, i) => ( + + {a}/ + {i < homepageConfig.areas.length - 1 ? ' ' : ''} + + ))} +
+
+ +
+
+ INDEX OF /writing + · + FILTER: + {AREAS.map(a => ( + + ))} +
+ + + + + + + + + + + + + + + + + + + + {entries.length === 0 ? ( + + + + ) : entries.map((entry) => ( + + + + + + + + ))} + +
DATETYPEAREATITLE
+ No entries found. +
{entry.dateLabel} + {entry.type} + {entry.area} + {entry.title} +
+ +
+ {entries.length} {area === 'all' ? 'total' : area} entr{entries.length === 1 ? 'y' : 'ies'} +
+
+ +
+
+ ); +} diff --git a/src/pages/homepage-preview.js b/src/pages/homepage-preview.js new file mode 100644 index 0000000..c0d1d6e --- /dev/null +++ b/src/pages/homepage-preview.js @@ -0,0 +1,30 @@ +import React from 'react'; +import Head from '@docusaurus/Head'; +import BrowserOnly from '@docusaurus/BrowserOnly'; + +export default function HomepagePreview() { + return ( + <> + + 5L Labs — Homepage Preview + + + + Loading preview…
}> + {() => { + const PreviewApp = require('../components/HomepageRedesign/PreviewApp').default; + return ; + }} + + + ); +} diff --git a/src/pages/index.js b/src/pages/index.js index 656fe2f..da59e0d 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -1,47 +1,166 @@ -import React from "react"; -import clsx from "clsx"; -import Link from "@docusaurus/Link"; -import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; -import Layout from "@theme/Layout"; -import HomepageContent from "../components/HomepageContent"; -import styles from "./index.module.css"; +import React, { useState, useMemo } from 'react'; +import Link from '@docusaurus/Link'; +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import Layout from '@theme/Layout'; +import homepageConfig from '../config/homepage'; +import allPosts from '../generated/all-posts.json'; +import styles from './index.module.css'; -import Logo from "@site/static/img/5L_Labs_Logo.png"; - -function HomepageHeader() { - const { siteConfig } = useDocusaurusContext(); - return ( -
-
-
- {/* Explicit width/height to prevent CLS and ensure proper aspect ratio */} - 5L Labs Logo -
-

{siteConfig.title}

-

{siteConfig.tagline}

-
-
-
-
- ); -} +const PREVIEW_COUNT = 7; +const AREAS = ['all', ...homepageConfig.areas]; export default function Home() { const { siteConfig } = useDocusaurusContext(); + const [area, setArea] = useState('all'); + + // ⚡ Bolt Perf: Memoize array filtering to prevent redundant O(N) operations on every component re-render unless the area filter changes. + const filtered = useMemo(() => area === 'all' ? allPosts : allPosts.filter(p => p.area === area), [area]); + const entries = filtered.slice(0, PREVIEW_COUNT); + const remaining = filtered.length - PREVIEW_COUNT; + + const { consulting, areas } = homepageConfig; + return ( - -
- +
+ + {/* Terminal banner */} +
+
$ whoami
+
5L Labs — commercial privacy-first research, est. 2023 (NYC)
+
$ cat mission.txt
+
{homepageConfig.missionStatement}
+
$ ls areas/
+
+ {areas.map(a => ( + {a}/ + ))} +
+
+ $ ./hire-us --consulting{' '} + +
+
+ + {/* Index */} +
+
+ INDEX OF / + · + FILTER: + {AREAS.map(a => ( + + ))} +
+ + + + + + + + + + + + + + + + + + + + {entries.length === 0 ? ( + + + + ) : entries.map((entry) => ( + + + + + + + + ))} + +
DATETYPEAREATITLE
+ No entries found. +
{entry.dateLabel} + {entry.type} + {entry.area} + {entry.title} +
+ + {remaining > 0 && ( +
+ + — {remaining} more → full archive — + +
+ )} +
+ + {/* Projects + Consulting */} +
+ +
+
~/projects
+ {homepageConfig.products.map((p) => { + const isExternal = p.link && p.link.startsWith('http'); + return ( + +
+ + {p.title.toLowerCase().replace(/\s*\(.*?\)/, '')} + + +
+
{p.description}
+ + ); + })} +
+ +
+
~/consulting
+
{consulting.blurb}
+
{consulting.services}
+
+ CURRENT AVAILABILITY:{' '} + {consulting.availability} +
+ + ./inquire → + +
+ +
); diff --git a/src/pages/index.module.css b/src/pages/index.module.css index 9f71a5d..6dcc2cd 100644 --- a/src/pages/index.module.css +++ b/src/pages/index.module.css @@ -1,23 +1,251 @@ -/** - * CSS files with the .module.css suffix will be treated as CSS modules - * and scoped locally. - */ +.page { + max-width: 1100px; + margin: 0 auto; + padding: 48px 40px 80px; + font-family: var(--font-mono); + font-size: 13px; + line-height: 1.55; +} -.heroBanner { - padding: 4rem 0; - text-align: center; +/* ── Terminal banner ── */ +.terminal { + padding: 20px 24px; + background: #0d1117; + color: #e8e6df; + font-family: var(--font-mono); + font-size: 13px; + line-height: 1.6; + border: 1px solid #30363d; + border-radius: 4px; + margin-bottom: 40px; +} +.terminalPrompt { color: #7bd27b; } +.terminalDir { color: #f0a060; } +.terminalCursor { color: #7bd27b; } + +/* Light mode: terminal needs to stand out from white bg */ +[data-theme='light'] .terminal { + background: #1a1a1a; + border-color: #333; +} + +/* ── Index ── */ +.indexSection { margin-bottom: 36px; } + +.indexHeader { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 12px; + color: var(--text-muted); + font-size: 11px; + letter-spacing: 0.14em; +} + +.indexTable { + width: 100%; + border-collapse: collapse; + font-family: var(--font-mono); + font-size: 13px; + table-layout: fixed; +} + +/* Column widths */ +.indexTable col.colDate { width: 108px; } +.indexTable col.colType { width: 96px; } +.indexTable col.colArea { width: 108px; } +.indexTable col.colTitle { width: auto; } +.indexTable col.colMeta { width: 80px; } + +.indexTable thead tr { + border-bottom: 1.5px solid var(--text-main); + text-align: left; +} +.indexTable thead th { + padding: 8px 6px; + font-weight: 500; + font-size: 11px; + letter-spacing: 0.08em; + color: var(--text-muted); + white-space: nowrap; +} +.indexTable thead th:last-child { text-align: right; } + +.indexTable tbody tr { position: relative; + border-bottom: 1px dashed color-mix(in srgb, var(--text-muted) 30%, transparent); +} +.indexTable tbody tr:hover { background: color-mix(in srgb, var(--text-muted) 8%, transparent); } +.indexTable tbody tr:focus-within { outline: 2px solid var(--brand-primary); outline-offset: -2px; } + +.indexTable tbody td { + padding: 10px 6px; + color: var(--text-muted); + vertical-align: middle; + white-space: nowrap; overflow: hidden; + text-overflow: ellipsis; +} +.indexTable tbody td.colTitle { + color: var(--text-main); + white-space: normal; +} +.indexTable tbody td.colTitle a { + color: var(--text-main); + text-decoration: none; +} +.indexTable tbody td.colTitle a::after { + content: ""; + position: absolute; + inset: 0; + z-index: 1; +} +.indexTable tbody td.colTitle a:hover { color: var(--brand-primary); } +.indexTable tbody td:last-child { text-align: right; } +.indexTable tbody td:last-child a { + display: inline-block; + transition: transform 0.2s ease, color 0.2s ease; + z-index: 2; + position: relative; +} +.indexTable tbody tr:hover td:last-child a, +.indexTable tbody tr:focus-within td:last-child a { + transform: translate(2px, -2px); + color: var(--brand-primary); } -@media screen and (max-width: 996px) { - .heroBanner { - padding: 2rem; - } +.loadMore { + text-align: center; + margin-top: 14px; + color: var(--text-muted); + font-size: 12px; } -.buttons { +/* ── Chips ── */ +.chip { + display: inline-block; + border: 1px solid color-mix(in srgb, var(--text-muted) 50%, transparent); + padding: 1px 7px; + font-size: 11px; + font-family: var(--font-mono); + border-radius: 999px; + background: transparent; + color: var(--text-muted); + cursor: pointer; + white-space: nowrap; +} +.chip:hover { + opacity: 0.8; +} +.chip:focus-visible { + outline: 2px solid var(--brand-primary); + outline-offset: 2px; +} + +.chipActive { + border-color: var(--brand-primary); + color: var(--brand-primary); +} + +/* ── Two-column lower section ── */ +.twoCol { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 24px; +} + +.box { + border: 1px solid color-mix(in srgb, var(--text-muted) 35%, transparent); + border-radius: 4px; + padding: 20px; +} + +.boxLabel { + font-size: 11px; + letter-spacing: 0.14em; + color: var(--brand-primary); + margin-bottom: 14px; +} + +.projectGroupLink { + display: block; + text-decoration: none; + padding: 10px 0; + border-bottom: 1px dashed color-mix(in srgb, var(--text-muted) 30%, transparent); +} +.projectGroupLink:last-child { border-bottom: none; padding-bottom: 0; } +.projectGroupLink:hover .projectName { color: var(--brand-primary); } +.projectGroupLink:focus-visible { + outline: 2px solid var(--brand-primary); + outline-offset: 4px; + border-radius: 2px; +} +.projectGroupLink:hover .projectMeta, +.projectGroupLink:focus-visible .projectMeta { + transform: translate(2px, -2px); + color: var(--brand-primary); +} + +.projectRow { display: flex; + justify-content: space-between; + align-items: baseline; + font-size: 14px; + margin-bottom: 3px; +} +.projectName { color: var(--text-main); text-decoration: none; transition: color 0.2s ease; } +.projectMeta { + color: var(--text-muted); + font-size: 12px; + display: inline-block; + transition: transform 0.2s ease, color 0.2s ease; +} +.projectDesc { font-size: 12px; color: var(--text-muted); transition: color 0.2s ease; } + +.consultTitle { + font-family: var(--font-mono); + font-size: 15px; + line-height: 1.4; + margin-bottom: 10px; + color: var(--text-main); +} +.consultDesc { + font-size: 13px; + color: var(--text-muted); + margin-bottom: 14px; +} +.availability { + font-size: 11px; + letter-spacing: 0.1em; + color: var(--text-muted); + margin-bottom: 14px; +} +.availabilityVal { color: var(--brand-primary); } + +.btnAccent { + display: inline-flex; align-items: center; - justify-content: center; + background: var(--brand-primary); + color: #fff; + border: none; + padding: 8px 16px; + font-family: var(--font-mono); + font-size: 13px; + border-radius: 3px; + text-decoration: none; + cursor: pointer; + box-shadow: 3px 3px 0 rgba(0,0,0,0.2); +} +.btnAccent:hover { color: #fff; opacity: 0.9; text-decoration: none; } +.btnAccent:focus-visible { + outline: 2px solid var(--brand-primary); + outline-offset: 2px; +} + + +@media (max-width: 768px) { + .page { padding: 32px 20px 60px; } + .twoCol { grid-template-columns: 1fr; } + .indexTable { font-size: 12px; table-layout: auto; } + .hideOnMobile { display: none; } } diff --git a/src/pages/inquiry.js b/src/pages/inquiry.js new file mode 100644 index 0000000..1d0f7af --- /dev/null +++ b/src/pages/inquiry.js @@ -0,0 +1,188 @@ +import React, { useState } from 'react'; +import Layout from '@theme/Layout'; +import Link from '@docusaurus/Link'; +import homepageConfig from '../config/homepage'; +import styles from './inquiry.module.css'; + +// To wire up form submission, create a free account at https://formspree.io, +// create a form, and replace null with your form ID string e.g. 'xpwzabcd' +const FORMSPREE_ID = null; + +const INITIAL = { name: '', email: '', phone: '', message: '', smsConsent: false }; + +export default function Inquiry() { + const [fields, setFields] = useState(INITIAL); + const [status, setStatus] = useState('idle'); // idle | submitting | success | error + + const set = (key) => (e) => { + const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value; + setFields(prev => ({ ...prev, [key]: value })); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + setStatus('submitting'); + + if (FORMSPREE_ID) { + try { + const res = await fetch(`https://formspree.io/f/${FORMSPREE_ID}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', Accept: 'application/json' }, + body: JSON.stringify({ + name: fields.name, + email: fields.email, + phone: fields.phone || '(not provided)', + message: fields.message, + sms_consent: fields.smsConsent ? 'Yes — consented to SMS' : 'No', + }), + }); + setStatus(res.ok ? 'success' : 'error'); + } catch { + setStatus('error'); + } + } else { + // Fallback: open email client with pre-filled body + const body = encodeURIComponent( + `Name: ${fields.name}\nEmail: ${fields.email}\nPhone: ${fields.phone || 'N/A'}\n\n${fields.message}\n\nSMS consent: ${fields.smsConsent ? 'Yes' : 'No'}` + ); + if (typeof window !== 'undefined') { + window.location.href = `mailto:${homepageConfig.contactInfo.email}?subject=Consulting%20Inquiry&body=${body}`; + } + setStatus('success'); + } + }; + + if (status === 'success') { + return ( + +
+
+
+

Got it.

+

We'll be in touch at {fields.email}.

+

We take on 1–2 engagements per quarter — expect a response within a few business days.

+ ← back to home +
+
+
+ ); + } + + return ( + +
+
+
~/consulting/inquire
+

Start an inquiry

+

+ We take 1–2 engagements per quarter. Current availability:{' '} + {homepageConfig.consulting.availability}. +

+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +